私がしたいことは、このようなものです:フラグ付き値を組み合わせたenumがあります。
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で公開されています。
(私の列挙型制約についてのブログ投稿。私はスタンドアロンの答えのために以下の基本的な事実を含んでいます。)
最善の解決策は、私がそれを制約のないメロディ1。これは、以下のような "偽の"制約を持つC#コードを取るライブラリです。
where T : struct, IEnumConstraint
それを
where T : struct, System.Enum
ポストビルドステップを介して
書き込むのは難しいことではありませんIsSet
...両方のケータリングInt64
ベースおよびUInt64
ベースのフラグは難しい部分になる可能性があります。 (私は基本的な型を持っているかのように、基本的にフラグの列挙型を扱うことを可能にするいくつかのヘルパーメソッドの臭いがあります。UInt64
。)
あなたが電話した場合の行動は何になりますか?
tester.IsSet(MyFlags.A | MyFlags.C)
?それをチェックすべきか?すべて指定されたフラグはセットされていますか?それが私の期待です。
私は今夜家に帰るときにこれをやろうとしています...ライブラリをすぐに使える標準にするための便利な列挙メソッドをすばやく用意してから、ちょっとリラックスしたいと思っています。
編集:私は確信していないIsSet
途中で名前として。オプション:
歓迎します。とにかく何かが石で飾られるまでには、しばらく時間がかかるでしょう...
1もちろん、パッチとして提出してください...
colors.HasAny(Colors.Red | Colors.Blue)
非常に読みやすいコードのように見えます。=)
- Blixt
Darrenは、型が特定の列挙型であれば動作します。一般的な列挙型はブール値演算を行うためにint型にキャストする必要があります。
public static bool IsSet( this Enum input, Enum matchTo )
{
return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0;
}
Convert.ToUInt32
私はどこにいても見つかりませんでした。 AFAIKは、VBでも動作する唯一のまともなPre-Net-4ソリューションです。ところで、ifmatchTo
複数のフラグビットを持つ可能性があります。!= 0
〜と== Convert.ToUInt32(matchTo)
。 - ToolmakerSteveConvert.ToUInt32
列挙型で使用されると、Convert.ToUInt32(object)
これは、CLRが最初にこれらの値をボックスに渡してから、ToUInt32
方法。ほとんどの場合、これは問題にはなりませんが、このような何かを使用して毎秒何百万もの列挙を解析すると、GCを忙しくすることを知っておきましょう。 - Groo
実際には、醜いトリックで可能です。 ただし、拡張メソッドには使用できません。
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")
あなたがしたい場合は、あなたは与えることができますEnums<Temp>
プライベートコンストラクタとパブリックネストされた抽象継承クラスTemp
としてEnum
継承されていないバージョンのenumを防ぐためです。
あなたはIL製織を使用してこれを達成することができます。余分な制約
このコードを書くことができます
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
{
}
}
C#7.3から、enum制約を組み込む組み込みの方法が追加されました。
public class UsingEnum<T> where T : System.Enum { }
これは元の質問には答えませんが、.NET 4ではメソッドがありますEnum.HasFlagあなたの例で何をしようとしているのか
flag
。 .NET 4.0は現在5歳です。 - Jeppe Stig Nielsen
私がそれを行う方法は、構造体の制約を入れて、実行時にTが列挙型であることを確認します。これで問題は完全に解消されるわけではありませんが、問題は多少軽減されます
C#7.3以降、汎用型に対してEnum制約を使用できます。
public static TEnum Parse<TEnum>(string value) where TEnum : Enum
{
return (TEnum) Enum.Parse(typeof(TEnum), value);
}
Nullable列挙型を使用する場合は、orginial構造体制約をそのままにする必要があります。
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;
}
}
ここで私がやったいくつかのコードは、あまりにも狂ったことをすることなくあなたが望むように動作するようです。 Flagsとして設定されたenumだけに限定されているわけではありませんが、必要であればいつでもチェックを入れることができます。
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.");
}
}
(EnumConstraintを使用している)Enum onfly変換に文字列を変換する必要がある場合は、次のようになります。
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));
}
}
これが誰かを助けることを望みます
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());
}
struct
ちょうど良い。 - Timwi