c# enum用法
任何人都知道缺少枚举泛型约束的好解决方法吗? (8)
我想做的是这样的:我有带有组合标记值的枚举。
public static class EnumExtension
{
public static bool IsSet<T>( this T input, T matchTo )
where T:enum //the constraint I want that doesn't exist in C#3
{
return (input & matchTo) != 0;
}
}
因此,我可以这样做:
MyEnum tester = MyEnum.FlagA | MyEnum.FlagB
if( tester.IsSet( MyEnum.FlagA ) )
//act on flag a
不幸的是,C#泛型的约束没有枚举约束,只有类和结构。 C#不会将枚举视为结构(即使它们是值类型),因此我无法添加这样的扩展类型。
有谁知道解决方法?
编辑:这现在存在于UnconstrainedMelody的0.0.0.2版本中。
(按照我的博客文章中有关枚举约束的要求 。为了提供独立答案,我在下面列出了基本事实。)
最好的解决方案是等待我将其包含在UnconstrainedMelody 1中 。 这是一个使用带有“假”约束的C#代码的库,例如
where T : struct, IEnumConstraint
并变成
where T : struct, System.Enum
通过后期构建步骤。
编写IsSet
应该不难...尽管同时满足基于Int64
和基于UInt64
的标志可能是棘手的部分。 (我闻到了一些辅助方法的出现,基本上使我可以将任何标志枚举都视为具有UInt64
的基本类型。)
如果您致电,您希望该行为如何?
tester.IsSet(MyFlags.A | MyFlags.C)
? 是否应该检查所有指定标志都已设置? 那是我的期望。
我将在今晚回家的路上尝试这样做。我希望对有用的枚举方法进行快速的探讨,以使库快速达到可用的标准,然后再放松一下。
编辑:顺便说一下,我不确定IsSet
为名称。 选项:
- 包括
- 包含
- HasFlag(或HasFlags)
- IsSet(肯定是一个选项)
欢迎思想。 我敢肯定,要把所有东西都固定下来还需要一段时间...
1或作为补丁提交,当然...
从C#7.3开始,可以对通用类型使用Enum约束:
public static TEnum Parse<TEnum>(string value) where TEnum : Enum
{
return (TEnum) Enum.Parse(typeof(TEnum), value);
}
如果要使用Nullable枚举,则必须保留原始struct约束:
public static TEnum? TryParse<TEnum>(string value) where TEnum : struct, Enum
{
if( Enum.TryParse(value, out TEnum res) )
return res;
else
return null;
}
使用原始代码,在方法内部,您还可以使用反射来测试T是否为枚举:
public static class EnumExtension
{
public static bool IsSet<T>( this T input, T matchTo )
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException("Must be an enum", "input");
}
return (input & matchTo) != 0;
}
}
如果有人需要通用的IsSet(可以即时改进开箱即用的创建方式),或需要将字符串转换为Enum onfly转换(使用下面显示的EnumConstraint):
public class TestClass
{ }
public struct TestStruct
{ }
public enum TestEnum
{
e1,
e2,
e3
}
public static class TestEnumConstraintExtenssion
{
public static bool IsSet<TEnum>(this TEnum _this, TEnum flag)
where TEnum : struct
{
return (((uint)Convert.ChangeType(_this, typeof(uint))) & ((uint)Convert.ChangeType(flag, typeof(uint)))) == ((uint)Convert.ChangeType(flag, typeof(uint)));
}
//public static TestClass ToTestClass(this string _this)
//{
// // #generates compile error (so no missuse)
// return EnumConstraint.TryParse<TestClass>(_this);
//}
//public static TestStruct ToTestStruct(this string _this)
//{
// // #generates compile error (so no missuse)
// return EnumConstraint.TryParse<TestStruct>(_this);
//}
public static TestEnum ToTestEnum(this string _this)
{
// #enum type works just fine (coding constraint to Enum type)
return EnumConstraint.TryParse<TestEnum>(_this);
}
public static void TestAll()
{
TestEnum t1 = "e3".ToTestEnum();
TestEnum t2 = "e2".ToTestEnum();
TestEnum t3 = "non existing".ToTestEnum(); // default(TestEnum) for non existing
bool b1 = t3.IsSet(TestEnum.e1); // you can ommit type
bool b2 = t3.IsSet<TestEnum>(TestEnum.e2); // you can specify explicite type
TestStruct t;
// #generates compile error (so no missuse)
//bool b3 = t.IsSet<TestEnum>(TestEnum.e1);
}
}
如果仍然有人需要示例来创建Enum编码约束:
using System;
/// <summary>
/// would be same as EnumConstraint_T<Enum>Parse<EnumType>("Normal"),
/// but writen like this it abuses constrain inheritence on System.Enum.
/// </summary>
public class EnumConstraint : EnumConstraint_T<Enum>
{
}
/// <summary>
/// provides ability to constrain TEnum to System.Enum abusing constrain inheritence
/// </summary>
/// <typeparam name="TClass">should be System.Enum</typeparam>
public abstract class EnumConstraint_T<TClass>
where TClass : class
{
public static TEnum Parse<TEnum>(string value)
where TEnum : TClass
{
return (TEnum)Enum.Parse(typeof(TEnum), value);
}
public static bool TryParse<TEnum>(string value, out TEnum evalue)
where TEnum : struct, TClass // struct is required to ignore non nullable type error
{
evalue = default(TEnum);
return Enum.TryParse<TEnum>(value, out evalue);
}
public static TEnum TryParse<TEnum>(string value, TEnum defaultValue = default(TEnum))
where TEnum : struct, TClass // struct is required to ignore non nullable type error
{
Enum.TryParse<TEnum>(value, out defaultValue);
return defaultValue;
}
public static TEnum Parse<TEnum>(string value, TEnum defaultValue = default(TEnum))
where TEnum : struct, TClass // struct is required to ignore non nullable type error
{
TEnum result;
if (Enum.TryParse<TEnum>(value, out result))
return result;
return defaultValue;
}
public static TEnum Parse<TEnum>(ushort value)
{
return (TEnum)(object)value;
}
public static sbyte to_i1<TEnum>(TEnum value)
{
return (sbyte)(object)Convert.ChangeType(value, typeof(sbyte));
}
public static byte to_u1<TEnum>(TEnum value)
{
return (byte)(object)Convert.ChangeType(value, typeof(byte));
}
public static short to_i2<TEnum>(TEnum value)
{
return (short)(object)Convert.ChangeType(value, typeof(short));
}
public static ushort to_u2<TEnum>(TEnum value)
{
return (ushort)(object)Convert.ChangeType(value, typeof(ushort));
}
public static int to_i4<TEnum>(TEnum value)
{
return (int)(object)Convert.ChangeType(value, typeof(int));
}
public static uint to_u4<TEnum>(TEnum value)
{
return (uint)(object)Convert.ChangeType(value, typeof(uint));
}
}
希望这对某人有帮助。
您可以使用IL Weaving和ExtraConstraints实现此目的
允许您编写此代码
public class Sample
{
public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
{
}
public void MethodWithEnumConstraint<[EnumConstraint] T>()
{
}
}
编译什么
public class Sample
{
public void MethodWithDelegateConstraint<T>() where T: Delegate
{
}
public void MethodWithEnumConstraint<T>() where T: struct, Enum
{
}
}
我只是想将Enum添加为通用约束。
虽然这只是使用ExtraConstraints
的一种辅助方法,但对我来说却有点过多的开销。
我决定只创建一个struct
约束并为IsEnum
添加运行时检查。 为了将变量从T转换为Enum,我首先将其转换为对象。
public static Converter<T, string> CreateConverter<T>() where T : struct
{
if (!typeof(T).IsEnum) throw new ArgumentException("Given Type is not an Enum");
return new Converter<T, string>(x => ((Enum)(object)x).GetEnumDescription());
}
达伦(Darren),如果类型是特定的枚举,那将起作用-为了使通用枚举起作用,您必须将它们转换为int(或更可能是uint)才能进行布尔数学运算:
public static bool IsSet( this Enum input, Enum matchTo )
{
return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0;
}
这不能回答原始问题,但是.NET 4中现在有一个名为Enum.HasFlag的方法,该方法可以完成您在示例中尝试做的事情