【问题标题】:Is there a way to set an Enumerated Property in a class to All available enums?有没有办法将类中的枚举属性设置为所有可用的枚举?
【发布时间】:2014-09-11 08:25:34
【问题描述】:

首先,我不知道该问题的标题是什么——我什至不知道如何陈述它。

现在回答问题。让我们使用 System.IO.FileSystemWatcher 类,您将其设置为 NotifyFilter 属性:

            this.FileSystemWatcher1.NotifyFilter = NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.FileName 
            | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Security 
            | NotifyFilters.Size;

设置单个属性的代码相当多。检查NotifyFilter,它是一个枚举。是否有一种“lazy”或“shortcut”方式来一次设置所有这些属性?我知道这不是必需的,但我的好奇心被激起了。

this.FileSystemWatcher1.NotifyFilter = <NotifyFilters.All>?

【问题讨论】:

  • 我更喜欢明确定义这种东西,例如在一个名为 All 的字段中。然后,您可以使用 ~ 排除特定的枚举值。
  • @GregRos 你能以某种方式证明你的意思吗?

标签: c# enums


【解决方案1】:

编写一个通用实用程序来屏蔽 c# 枚举中的所有值比人们想象的要困难得多,因为

  1. 枚举的底层类型可以是byte、short、int或long;签名或未签名。
  2. 无法将对象直接转换为通用枚举,可能是因为 C# 中没有内置枚举约束。必须先装箱,然后再拆箱。
  3. 所有枚举实用程序都来自 c# 1.0,因此相当粗糙。

这是我能做的最好的。我使用了以下内容:

  1. Cast Int to Generic Enum in C#
  2. Enum type constraints in C#

这在 c++/CLI 中会更容易吗?

/// <summary>
/// Contains generic utilities for enums, constrained for enums only.
/// </summary>
public sealed class EnumHelper : Enums<Enum>
{
    private EnumHelper()
    {
    }
}

/// <summary>
/// For use by EnumHelper, not for direct use.
/// </summary>
public abstract class Enums<TEnumBase> where TEnumBase : class, IConvertible
{
    private static void ThrowOnEnumWithoutFlags<TEnum>() where TEnum : struct, TEnumBase
    {
        var attributes = typeof(TEnum).GetCustomAttributes(typeof(FlagsAttribute), false);
        if (attributes.Length == 0)
        {
            throw (new ArgumentException("The generic argument [<T>] must be an enumeration with the [FlagsAttribute] applied.", "T: " + typeof(TEnum).FullName));
        }
    }

    public static TEnum GetAll<TEnum>() where TEnum : struct, TEnumBase
    {
        ThrowOnEnumWithoutFlags<TEnum>();
        var underlyingType = Enum.GetUnderlyingType(typeof(TEnum));
        if (underlyingType == typeof(ulong))
        {
            ulong value = 0;
            foreach (var v in Enum.GetValues(typeof(TEnum)))
                // Not sure I need the culture but Microsoft passes it in Enum.ToUInt64(Object value) - http://referencesource.microsoft.com/#mscorlib/system/enum.cs
                value |= Convert.ToUInt64(v, CultureInfo.InvariantCulture); 
            return (TEnum)Enum.ToObject(typeof(TEnum), value);
        }
        else
        {
            long value = 0;
            foreach (var v in Enum.GetValues(typeof(TEnum)))
                // Not sure I need the culture but Microsoft passes it in Enum.ToUInt64(Object value) - http://referencesource.microsoft.com/#mscorlib/system/enum.cs
                value |= Convert.ToInt64(v, CultureInfo.InvariantCulture);
            return (TEnum)Enum.ToObject(typeof(TEnum), value);
        }
    }

我已经测试过 byte、sbyte、short、int、long、ushort、uint 和 ulong 类型。

更新按照 hvd 的建议进行了简化。

【讨论】:

  • +1 您确实需要 longulong 的单独处理程序来处理超出 long 范围的值,但对于所有其他类型,您可以使用 long版本:只需使用Convert.ToInt64Enum.ToObject 来执行转换。 (为了保持一致性,您可以使用Convert.ToUInt64Enum.ToObject 来表示ulong 版本。)
【解决方案2】:

你总是可以做这样的事情,

NotifyFilter ret = 0;
foreach(NotifyFilter v in Enum.GetValues(typeof(NotifyFilter)))
{
    ret |= v;   
}

不幸的是,我不知道更好的方法。但是你总是可以把它放在一个通用的实用方法中。

private static T GetAll<T>() where T : struct, IConvertible
{
    if (!typeof(T).IsEnum)
    {
       throw new NotSupportedException(); // You'd want something better here, of course.
    }

    long ret = 0; // you could determine the type with reflection, but it might be easier just to use multiple methods, depending on how often you tend to use it.
    foreach(long v in Enum.GetValues(typeof(T)))
    {
        ret |= v;
    }

    return (T)ret;
}

【讨论】:

  • 仅供参考,您可以说foreach(NotifyFilter in Enum.GetValues(typeof(NotifyFilter))) 将演员表保存在 foreach 体内。
  • 好一个。我正在处理一些主要的个人项目,并希望保持我的应用程序快速。我没有想到使用通用方法来解决这个问题。
  • T 限制为where T: struct, IConvertible 并检查if (!typeof(T).IsEnum) { throw ... }
  • (T)ret 无法为我编译——但我​​坚持使用 c# 3.0。它适用于 4.0/4.5 吗?
  • @Noobgrammer 也许可以设置所有标志,即使是那些未使用或未命名的标志。在这种情况下,您只需执行this.FileSystemWatcher1.NotifyFilter = (NotifyFilters)(~0); 或(等效)this.FileSystemWatcher1.NotifyFilter = (NotifyFilters)(-1);。但一定要检查NotifyFilters的底层类型是否为intit is)。
【解决方案3】:

如果枚举应用了Flags 属性,您可以添加与所需枚举成员等效的数字,然后分配它。

在你问题的例子中,总结 4+64+32+16+8 = 124 然后你可以写

this.FileSystemWatcher1.NotifyFilter = (NotifyFilters) 124;

当然,这会降低枚举的可读性并严重导致维护问题,但正如您所说,这是为我们的懒人准备的:

public class Program
{
    [Flags]
    enum Enum:int
    {
        a = 1,
        b = 2
    }

    static void Main(string[] args)
    {
        Enum x = (Enum) 3;
        Console.WriteLine(x.ToString());

    }
}

输出:

a,b

更新:

我差点忘了。您当然可以将此字符串传递给Enum.Parse 并获得所需的值:

this.FileSystemWatcher1.NotifyFilter = (NotifyFilter) Enum.Parse(typeof(NotifyFilter), 
"Attributes,CreationTime,FileName,LastAccess,LastWrite,Security,Size"); 

【讨论】:

  • 这很奇怪。不知道你能做到这一点
  • 它仍然把我吓坏了,但正如你所说:好奇心,当然还有懒惰。
  • 我可以想象这个在意大利面条代码中的着陆,以及下一个开发人员从 27 层楼的建筑中跳下
  • 没错。我自己讨厌这个,但在一些紧张的地方会派上用场。
【解决方案4】:

如果不编写自己的方法,就没有办法——正如其他地方正确回答的那样。

如果枚举是你要更改的,你可以添加一个新值

All = ~0

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-17
    • 2016-07-24
    • 1970-01-01
    相关资源
    最近更新 更多