C# 任何人都知道缺少枚举通用约束的好方法吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7244/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Anyone know a good workaround for the lack of an enum generic constraint?
提问by Keith
What I want to do is something like this: I have enums with combined flagged values.
我想要做的是这样的事情:我有带有组合标记值的枚举。
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;
}
}
So then I could do:
那么我可以这样做:
MyEnum tester = MyEnum.FlagA | MyEnum.FlagB
if( tester.IsSet( MyEnum.FlagA ) )
//act on flag a
Unfortunately, C#'s generic where constraints have no enum restriction, only class and struct. C# doesn't see enums as structs (even though they are value types) so I can't add extension types like this.
不幸的是,C# 的泛型约束没有枚举限制,只有类和结构。C# 不会将枚举视为结构(即使它们是值类型),因此我无法添加这样的扩展类型。
Does anyone know a workaround?
有谁知道解决方法?
采纳答案by Jon Skeet
EDIT: This is now live in version 0.0.0.2 of UnconstrainedMelody.
编辑:这现在在 UnconstrainedMelody 的 0.0.0.2 版中。
(As requested on my blog post about enum constraints. I've included the basic facts below for the sake of a standalone answer.)
(根据我关于 enum 约束的博客文章的要求。为了独立回答,我在下面包含了基本事实。)
The best solution is to wait for me to include it in UnconstrainedMelody1. This is a library which takes C# code with "fake" constraints such as
最好的解决方案是等待我将它包含在UnconstrainedMelody 1 中。这是一个库,它采用带有“假”约束的 C# 代码,例如
where T : struct, IEnumConstraint
and turns it into
并将其变成
where T : struct, System.Enum
via a postbuild step.
通过后期构建步骤。
It shouldn't be too hard to write IsSet
... although catering for both Int64
-based and UInt64
-based flags could be the tricky part. (I smell some helper methods coming on, basically allowing me to treat any flags enum as if it had a base type of UInt64
.)
写起来应该不会太难IsSet
……尽管同时满足Int64
基于和UInt64
基于的标志可能是棘手的部分。(我闻到了一些辅助方法,基本上允许我将任何标志枚举视为它的基本类型为UInt64
.)
What would you want the behaviour to be if you called
如果你打电话,你希望行为是什么
tester.IsSet(MyFlags.A | MyFlags.C)
? Should it check that allthe specified flags are set? That would be my expectation.
? 它是否应该检查是否设置了所有指定的标志?那将是我的期望。
I'll try to do this on the way home tonight... I'm hoping to have a quick blitz on useful enum methods to get the library up to a usable standard quickly, then relax a bit.
我会在今晚回家的路上尝试这样做......我希望对有用的枚举方法进行快速闪电战,以使库快速达到可用的标准,然后放松一下。
EDIT: I'm not sure about IsSet
as a name, by the way. Options:
编辑:IsSet
顺便说一下,我不确定作为一个名字。选项:
- Includes
- Contains
- HasFlag (or HasFlags)
- IsSet (it's certainly an option)
- 包括
- 包含
- HasFlag(或 HasFlags)
- IsSet(这当然是一个选项)
Thoughts welcome. I'm sure it'll be a while before anything's set in stone anyway...
欢迎提出想法。无论如何,我相信在任何事情都一成不变之前还需要一段时间......
1or submit it as a patch, of course...
1或将其作为补丁提交,当然...
回答by Ronnie
Darren, that would work if the types were specific enumerations - for general enumerations to work you have to cast them to ints (or more likely uint) to do the boolean math:
Darren,如果类型是特定枚举,那将起作用 - 要使一般枚举起作用,您必须将它们强制转换为 ints(或更可能是 uint)以进行布尔数学运算:
public static bool IsSet( this Enum input, Enum matchTo )
{
return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0;
}
回答by Scott Dorman
Using your original code, inside the method you can also use reflection to test that T is an enum:
使用您的原始代码,在该方法中,您还可以使用反射来测试 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;
}
}
回答by thecoop
The way I do it is put a struct constraint, then check that T is an enum at runtime. This doesn't eliminate the problem completely, but it does reduce it somewhat
我这样做的方法是放置一个结构约束,然后在运行时检查 T 是否为枚举。这并不能完全消除问题,但确实可以减少一些问题
回答by SLaks
Actually, it is possible, with an ugly trick. However, it cannot be used for extension methods.
实际上,这是可能的,但有一个丑陋的技巧。但是,它不能用于扩展方法。
public abstract class Enums<Temp> where Temp : class {
public static TEnum Parse<TEnum>(string name) where TEnum : struct, Temp {
return (TEnum)Enum.Parse(typeof(TEnum), name);
}
}
public abstract class Enums : Enums<Enum> { }
Enums.IsSet<DateTimeKind>("Local")
If you want to, you can give Enums<Temp>
a private constructor and a public nested abstract inherited class with Temp
as Enum
, to prevent inherited versions for non-enums.
如果需要,您可以Enums<Temp>
使用Temp
as提供一个私有构造函数和一个公共嵌套抽象继承类Enum
,以防止非枚举的继承版本。
回答by Brian Surowiec
Here's some code that I just did up that seems to work like you want without having to do anything too crazy. It's not restricted to only enums set as Flags, but there could always be a check put in if need be.
这是我刚刚完成的一些代码,似乎可以像您想要的那样工作,而无需做任何太疯狂的事情。它不仅限于设置为标志的枚举,但如果需要,总是可以进行检查。
public static class EnumExtensions
{
public static bool ContainsFlag(this Enum source, Enum flag)
{
var sourceValue = ToUInt64(source);
var flagValue = ToUInt64(flag);
return (sourceValue & flagValue) == flagValue;
}
public static bool ContainsAnyFlag(this Enum source, params Enum[] flags)
{
var sourceValue = ToUInt64(source);
foreach (var flag in flags)
{
var flagValue = ToUInt64(flag);
if ((sourceValue & flagValue) == flagValue)
{
return true;
}
}
return false;
}
// found in the Enum class as an internal method
private static ulong ToUInt64(object value)
{
switch (Convert.GetTypeCode(value))
{
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
return (ulong)Convert.ToInt64(value, CultureInfo.InvariantCulture);
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return Convert.ToUInt64(value, CultureInfo.InvariantCulture);
}
throw new InvalidOperationException("Unknown enum type.");
}
}
回答by Phil Devaney
This doesn't answer the original question, but there is now a method in .NET 4 called Enum.HasFlagwhich does what you are trying to do in your example
这并没有回答最初的问题,但现在 .NET 4 中有一个名为Enum.HasFlag的方法,它可以执行您在示例中尝试执行的操作
回答by Simon
You can achieve this using IL Weaving and ExtraConstraints
您可以使用 IL Weaving 和ExtraConstraints实现此目的
Allows you to write this code
允许您编写此代码
public class Sample
{
public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
{
}
public void MethodWithEnumConstraint<[EnumConstraint] T>()
{
}
}
What gets compiled
什么被编译
public class Sample
{
public void MethodWithDelegateConstraint<T>() where T: Delegate
{
}
public void MethodWithEnumConstraint<T>() where T: struct, Enum
{
}
}
回答by Jürgen Steinblock
I just wanted to add Enum as a generic constraint.
我只是想将 Enum 添加为通用约束。
While this is just for a tiny helper method using ExtraConstraints
is a bit too much overhead for me.
虽然这只是用于一个很小的辅助方法,ExtraConstraints
但对我来说开销太大了。
I decided to just just create a struct
constraint and add a runtime check for IsEnum
. For converting a variable from T to Enum I cast it to object first.
我决定只创建一个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());
}
回答by SoLaR
if someone needs generic IsSet (created out of box on fly could be improved on), and or string to Enum onfly conversion (which uses EnumConstraint presented below):
如果有人需要通用的 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);
}
}
If someone still needs example hot to create Enum coding constraint:
如果有人仍然需要示例 hot 来创建 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));
}
}
hope this helps someone.
希望这能帮助到某个人。