19

我有这个枚举:

[Flags]
public enum ExportFormat
{
    None = 0,
    Csv = 1,
    Tsv = 2,
    Excel = 4,
    All = Excel | Csv | Tsv
}

我正在尝试对此(或任何真正的)枚举进行包装,以通知更改。目前它看起来像这样:

public class NotifyingEnum<T> : INotifyPropertyChanged
    where T : struct
{
    private T value;

    public event PropertyChangedEventHandler PropertyChanged;

    public NotifyingEnum()
    {
        if (!typeof (T).IsEnum)
            throw new ArgumentException("Type T must be an Enum");
    }

    public T Value
    {
        get { return value; }
        set
        {
            if (!Enum.IsDefined(typeof (T), value))
                throw new ArgumentOutOfRangeException("value", value, "Value not defined in enum, " + typeof (T).Name);

            if (!this.value.Equals(value))
            {
                this.value = value;

                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null)
                    handler(this, new PropertyChangedEventArgs("Value"));
            }
        }
    }
}

由于实际上可以为枚举分配任何值,因此我想检查给定的值是否已定义。但是我发现了一个问题。如果我在这里给它一个由例如组成的枚举Csv | Excel,那么Enum.IsDefined将返回false。显然是因为我没有定义任何由这两个组成的枚举。我想这在某种程度上是合乎逻辑的,但是我应该如何检查给定值是否有效?换句话说,要让它工作,我需要用什么交换下面的行?

if (!Enum.IsDefined(typeof (T), value))
4

11 回答 11

11

我们知道转换为字符串的枚举值永远不会以数字开头,但具有无效值的枚举值总是会。这是最简单的解决方案:

public static bool IsDefinedEx(this Enum yourEnum)
{
    char firstDigit = yourEnum.ToString()[0];
    if (Char.IsDigit(firstDigit) || firstDigit == '-')  // Account for signed enums too..
        return false;

    return true;
}

使用该扩展方法而不是股票 IsDefined 应该可以解决您的问题。

于 2012-01-12T00:01:49.683 回答
8

使用基于标志的枚举,它是关于是否设置位。因此对于“ExportFormat”,如果设置了第 1 位,则它是 CSV 格式,即使可能设置了更多位。位 1 和 2 是否设置了无效值?这是主观的:从作为一个组的值的角度来看,它是无效的(没有为第 1 位和第 2 位设置定义位模式)但是,由于每个值都是一个位,单独查看它们,可能是设置了位 1 和 2 的值是有效的。

如果传入值 0011111011,这是一个有效值吗?好吧,这取决于您要查找的内容:如果您正在查看整个值,那么它是一个无效值,但如果您正在查看单个位,这是一个好的值:它设置了未设置的位已定义,但这没关系,因为基于标志的枚举是“按位”检查的:您不是在将它们与一个值进行比较,而是在检查是否设置了一个位。

因此,由于您的逻辑将检查设置了哪些位以选择要选择的格式,因此实际上没有必要检查枚举值是否已定义:您有 3 种格式:如果设置了相应格式的位,则格式为选择。这就是你应该写的逻辑。

于 2009-02-09T09:22:06.073 回答
5

我将在位级别上进行操作,并检查新值中设置的所有位是否都设置在您的All值中:

if ( ! (All & NewValue) == NewValue )

您将不得不看看自己如何最好地做到这一点,也许您需要将所有值转换为 int 然后进行按位比较。

于 2009-02-09T09:22:08.287 回答
2

是一个有效的小扩展方法。

static void Main(string[] args)
{
  var x = ExportFormat.Csv | ExportFormat.Excel;
  var y = ExportFormat.Csv | ExportFormat.Word;
  var z = (ExportFormat)16; //undefined value

  var xx = x.IsDefined();  //true
  var yy = y.IsDefined();  //false
  var zz = z.IsDefined();  //false
}

public static bool IsDefined(this Enum value)
{
  if (value == null) return false;
  foreach (Enum item in Enum.GetValues(value.GetType()))
    if (item.HasFlag(value)) return true;
  return false;
}

[Flags]
public enum ExportFormat                                      
{
  None = 0,
  Csv = 1,
  Tsv = 2,
  Excel = 4,
  Word = 8,
  All = Excel | Csv | Tsv
}

以下方法适用于未在枚举中分组的按代码组合的项目:

static void Main(string[] args)
{
  var x = ExportFormat.Csv | ExportFormat.Excel;
  var y = ExportFormat.Csv | ExportFormat.Word;
  var z = (ExportFormat)16; //undefined value

  var xx = x.IsDefined();  //true
  var yy = y.IsDefined();  //true
  var zz = z.IsDefined();  //false
}

public static bool IsDefined(this ExportFormat value)
{
  var max = Enum.GetValues(typeof(ExportFormat)).Cast<ExportFormat>()
    .Aggregate((e1,e2) =>  e1 | e2);
  return (max & value) == value;
}

如果您使用的是支持 DLR 的 C# 4.0,您可以使用以下很酷的不可知扩展方法:

public static bool IsDefined(this Enum value)
{
  dynamic dyn = value;
  var max = Enum.GetValues(value.GetType()).Cast<dynamic>().
    Aggregate((e1,e2) =>  e1 | e2);
  return (max & dyn) == dyn;
}

注意 - 必须这样做,因为:

  1. 运算符|and&不能应用于类型Enum和的操作数Enum
  2. 这些运算符是在编译器中定义的并且没有被反射,所以没有办法用反射/Linq 表达式来检索它们,相信我 - 我已经尝试了这一切......
于 2012-01-12T00:22:15.477 回答
2
[Flags] enum E { None = 0, A = '1', B = '2', C = '4' }

public static bool IsDefined<T>(T value) where T : Enum
{
    var values = Enum.GetValues(typeof(T)).OfType<dynamic>().Aggregate((e1, e2) => (e1 | e2));

    return (values & value) == value;
}

// IsDefined(ExportFormat.Csv); // => True
// IsDefined(ExportFormat.All); // => True
// IsDefined(ExportFormat.All | ExportFormat.None); // => True
// IsDefined(ExportFormat.All | ExportFormat.Csv); // => True
// IsDefined((ExportFormat)16); // => False
// IsDefined((ExportFormat)int.MaxValue); // => False

// IsDefined(E.A); // => True
// IsDefined(E.A | E.B); // => True
// IsDefined((E)('1' | '2')); // => True
// IsDefined((E)('5')); // => True
// IsDefined((E)5); // => True
// IsDefined((E)8); // => False
// IsDefined((E)int.MaxValue); // => False
于 2018-12-03T13:58:16.000 回答
2

枚举值的任何有效组合都会产生一个非数字值:

public static class EnumExtensions
{
    public static bool IsDefined(this Enum value) => !ulong.TryParse(value.ToString(), out _);
}
于 2019-12-03T11:12:06.143 回答
1

也许尝试用解析捕获?
您不想传递哪些值?

    public T Value
    {
        get { return value; }
        set
        {
            try
            {
                Enum.Parse(typeof(T), value.ToString());
            }
            catch 
            {
                throw new ArgumentOutOfRangeException("value", value, "Value not defined in enum, " + typeof(T).Name);
            }
            if (!this.value.Equals(value))
            {
                this.value = value;

                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null)
                    handler(this, new PropertyChangedEventArgs("Value"));
            }
        }
    }
于 2009-02-09T10:50:00.947 回答
1

我知道这个帖子已经有一段时间没有得到回答了,但我认为使用内置函数回答它对于那些在我之后访问它的人来说是件好事。

使用 OP 的原始枚举,您可以使用以下代码解析位掩码值。

    ExportFormat format;
    if (!Enum.TryParse<ExportFormat>(value.ToString(), out format))
    {
      // Could not parse
    }

希望有帮助。

于 2011-08-11T17:54:21.700 回答
1

这是一种方法(使用Linq):

    private static bool IsDefined<T>(long value) where T : struct
    {
        var max = Enum.GetValues(typeof(T)).Cast<T>()
            .Select(v => Convert.ToInt64(v)).
            Aggregate((e1, e2) => e1 | e2);
        return (max & value) == value;
    }
于 2018-03-19T14:32:07.400 回答
1

看一下Enums.NET库的IsValid方法:

var validExportFormat = ExportFormat.Excel | ExportFormat.Csv;
validExportFormat.IsValid(EnumValidation.IsValidFlagCombination); // => true

var invalidExportFormat = (ExportFormat)100;
invalidExportFormat.IsValid(EnumValidation.IsValidFlagCombination); // => false
于 2019-11-22T10:27:46.177 回答
0

这里。相当多的代码。

于 2009-02-09T09:15:46.120 回答