この質問には既に回答があります:
更新:C#の回避策については、この質問の下部を参照してください。
こんにちは、
次の拡張メソッドを考えてみましょう。
public static bool HasFlags<T>(this T value, T flags)
where T : System.Enum
{
// ...
}
これは、クラスが通常継承を許可されていないため、コンパイル時にエラーをスローしますSystem.Enum
。問題は、指定された列挙体がenum
キーワードは実際に継承していますSystem.Enum
上のコードは、拡張メソッドを列挙型に限定する理想的な方法です。
今ここでの明らかな回避策は、Enum
の代わりにT
しかし、あなたはジェネリック型の利点を失います:
MyEnum e;
e.HasFlags(MyOtherEnum.DoFunkyStuff);
上記のコードはジェネリック型を使用してコンパイル時エラーをスローしますが、Enum
私はそうするために実装している場合)
制約チェックを無効にするために使用できるコンパイラオプションはありますか?これを行うには他にも素晴らしい方法がありますか?
それが示唆される前に、私は使用しないと言っていますwhere T : struct
またはいくつかのような、以来、あなたは奇妙なものを行うことができるだろう123.HasFlags(456)
。
私はなぜこのエラーが全く存在するのか困惑しています...あなたが使っているのと同じ問題ですwhere T : System.Object
しかし、それはあなたが持っているwhere T : class
...なぜそうでないwhere T : enum
?
C#の回避策
Jon Skeetは、クラスをコンパイルして
IEnumConstraint
これを次に、System.Enum
ポストビルド。これは、現時点でこの問題を回避するために最も近い人が近づくことができると私は信じています。見る:
- コードプロジェクト:http://code.google.com/p/unconstrained-melody/
- ブログエントリー:http://msmvps.com/blogs/jon_skeet/archive/2009/09/10/generic-constraints-for-enums-and-delegates.aspx
この回避策が実現できない場合は、ライブラリをC ++ / CLIコードとして記述する必要があります。これはジェネリック型の制約に使用できるものを制限しません(下記の私の答えのコードを参照してください)。
編集:ildasm / ilasmでこれをサポートするライブラリが利用可能になりました:制約のないメロディ。
C#チームのメンバーは、以前は好きなサポートできるようにするwhere T : Enum
そしてwhere T : Delegate
しかし、それは決して十分高い優先順位ではなかったことを意味します。 (私は推論が最初の場所で制限を受けていることは確かではありませんが、確かに...)
C#の最も実用的な回避策は次のとおりです。
public static bool HasFlags<T>(this T value, T flags) where T : struct
{
if (!(value is Enum))
{
throw new ArgumentException();
}
// ...
}
コンパイル時に "enum-ness"のチェックが失われますが、両方の場所で同じタイプを使用していることを確認することができます。もちろん、チェックの実行時のペナルティもあります。静的コンストラクタで例外をスローする実装に汎用のネストされた型を使用すると、最初の呼び出し後に実行時のペナルティを回避できます。
public static bool HasFlags<T>(this T value, T flags) where T : struct
{
if (!(value is Enum))
{
throw new ArgumentException();
}
return EnumHelper<T>.HasFlags(value, flags);
}
private class EnumHelper<T> where T : struct
{
static EnumHelper()
{
if (!typeof(Enum).IsAssignableFrom(typeof(T))
{
throw new InvalidOperationException(); // Or something similar
}
}
internal static HasFlags(T value, T flags)
{
...
}
}
Grecoが言及しているように、C ++ / CLIでメソッドを記述してから、別のオプションとしてC#のクラスライブラリを参照できます。
where T : struct
私の質問で言及された理由のために(123.HasFlags(456)
)私はGrecoのソリューションが好きですが、1つのメソッドのライブラリ全体の作成を正当化できません。 「注入する」プロセスをセットアップすることは可能であるか?コンパイル時にC ++ / CLIをC#ライブラリに追加する - Blixt
それには回避策があります
実際には、醜いトリックで可能です。 ただし、拡張メソッドには使用できません。
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.Parse<DateTimeKind>("Local")
あなたがしたい場合は、あなたは与えることができますEnums<Temp>
プライベートコンストラクタとパブリックネストされた抽象継承クラスTemp
としてEnum
継承されていないバージョンのenumを防ぐためです。
私はC + +の回避策に行くことに抵抗することができませんでしたし、私はそれを仕事に持って以来、私はあなたの残りの部分と共有するだろうと思った!
ここではC ++のコードです(私のC ++は非常に錆びていますので、特に引数がどのように定義されているか、エラーを指摘してください):
#include "stdafx.h"
using namespace System;
using namespace System::Runtime::CompilerServices;
namespace Blixt
{
namespace Utilities
{
[Extension]
public ref class EnumUtility abstract sealed
{
public:
generic <typename T> where T : value class, Enum
[Extension]
static bool HasFlags(T value, T flags)
{
__int64 mask = Convert::ToInt64(flags);
return (Convert::ToInt64(value) & mask) == mask;
}
};
}
}
テスト用のC#コード(コンソールアプリケーション):
using System;
using Blixt.Utilities;
namespace Blixt.Playground
{
[Flags]
public enum Colors : byte
{
Black = 0,
Red = 1,
Green = 2,
Blue = 4
}
[Flags]
public enum Tastes : byte
{
Nothing = 0,
Sour = 1,
Sweet = 2,
Bitter = 4,
Salty = 8
}
class Program
{
static void Main(string[] args)
{
Colors c = Colors.Blue | Colors.Red;
Console.WriteLine("Green and blue? {0}", c.HasFlags(Colors.Green | Colors.Red));
Console.WriteLine("Blue? {0}", c.HasFlags(Colors.Blue));
Console.WriteLine("Green? {0}", c.HasFlags(Colors.Green));
Console.WriteLine("Red and blue? {0}", c.HasFlags(Colors.Red | Colors.Blue));
// Compilation error:
//Console.WriteLine("Sour? {0}", c.HasFlags(Tastes.Sour));
Console.WriteLine("Press any key to exit...");
Console.ReadKey(true);
}
}
}
あなたはIL製織を使用してこれを達成することができます。余分な制約
このコードを書くことができます
public static bool HasFlags<[EnumConstraint] T>(this T value, T flags)
{
// ...
}
コンパイルされるもの
public static bool HasFlags<T>(this T value, T flags)
where T : System.Enum
{
// ...
}