81

私がしたいことは、このようなものです:フラグ付き値を組み合わせた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#は列挙型を(たとえ値型であっても)構造体として見ないので、このような拡張型を追加することはできません。

誰でも回避策を知っていますか?


  • Keith:UnconstrainedMelodyのバージョン0.0.0.2をダウンロード - HasAllとHasAnyを実装しました。楽しい。 - Jon Skeet
  • 「C#ではenumを構造体として表示していません」という意味はどうですか? enum型は、型パラメータとして使用できます。structちょうど良い。 - Timwi
  • ここでこの記事をチェックしてください:codeproject.com/KB/cs/ExtendEnum.aspx' IsValidEnumValue' ' IsFlagsEnumDefined'おそらくあなたの質問への答えがメソッドです。 - dmihailescu
  • これに投票してくださいユーザーボイスのアイデアあなたがそれを一日で.netに組み込みたいのであれば、 - Matthieu
  • C#7.3列挙型制約を導入します。 - Marc Sigrist

12 답변


47

編集:これは現在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途中で名前として。オプション:

  • 含まれるもの
  • 含有
  • HasFlag(またはHasFlags)
  • IsSet(確かにオプションです)

歓迎します。とにかく何かが石で飾られるまでには、しばらく時間がかかるでしょう...


1もちろん、パッチとして提出してください...


  • あなたはPostSharp LOLに行く必要があります:opostsharp.org/blog/generic-constraints-for-enums-and-delegates - Sam Harwell
  • 実際にはより単純なHasAny()とHasAll() - Keith
  • はい、私はそれがさらに良いことに同意します。colors.HasAny(Colors.Red | Colors.Blue)非常に読みやすいコードのように見えます。=) - Blixt
  • うん、私はHasAnyとHasAllも好きです。それと一緒に行くだろう。 - Jon Skeet
  • HasAnyとHasAllは素晴らしいようです。 - IDisposable

16

Darrenは、型が特定の列挙型であれば動作します。一般的な列挙型はブール値演算を行うためにint型にキャストする必要があります。

public static bool IsSet( this Enum input, Enum matchTo )
{
    return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0;
}


  • ばかげた数のフラグがある場合は、引数のGetTypeCode()とConvert.ToUint64()を呼び出すことができます。 - Kit
  • 素晴らしいですが、「Enum」とConvert.ToUInt32私はどこにいても見つかりませんでした。 AFAIKは、VBでも動作する唯一のまともなPre-Net-4ソリューションです。ところで、ifmatchTo複数のフラグビットを持つ可能性があります。!= 0〜と== Convert.ToUInt32(matchTo)。 - ToolmakerSteve
  • ご了承くださいConvert.ToUInt32列挙型で使用されると、Convert.ToUInt32(object)これは、CLRが最初にこれらの値をボックスに渡してから、ToUInt32方法。ほとんどの場合、これは問題にはなりませんが、このような何かを使用して毎秒何百万もの列挙を解析すると、GCを忙しくすることを知っておきましょう。 - Groo

9

実際には、醜いトリックで可能です。 ただし、拡張メソッドには使用できません。

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を防ぐためです。


8

あなたは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
    {
    }
}


8

C#7.3から、enum制約を組み込む組み込みの方法が追加されました。

public class UsingEnum<T> where T : System.Enum { }

ソース:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/where-generic-type-constraint


  • 私は私の記事を投稿する前にあなたの答えを見ませんでした。 +1 - Mik
  • "受け入れられた"最も広く見られる答えは、言語の変化を反映するためにもはや最新ではありません。最もよく知られている答えを知らず、ただ単にスキミングしている人は、最も関連性のある最新の情報を得ることはできません。 - ForeverZer0
  • @ ForeverZer0私は、ほとんどの人が、答えがかなり古くなると、それよりも優れたものが存在するかもしれないことを知っていると思います。 - Mik
  • @Mik明らかに、大声で考えていただけだった。それを指摘していただきありがとうございます。現在のところ、4つの答えがあるので、ちょうど発言していた。 - ForeverZer0

4

これは元の質問には答えませんが、.NET 4ではメソッドがありますEnum.HasFlagあなたの例で何をしようとしているのか


  • この時点では、ほとんどの人が.NET 4(またはそれ以上)を使用する必要がありますので、一緒にハックしようとするのではなく、このメソッドを使用する必要があるためです。 - CptRobby
  • Upvoted。しかし、彼らの解決策は引数のボクシングを使用してflag。 .NET 4.0は現在5歳です。 - Jeppe Stig Nielsen

3

私がそれを行う方法は、構造体の制約を入れて、実行時にTが列挙型であることを確認します。これで問題は完全に解消されるわけではありませんが、問題は多少軽減されます


  • T:struct、IComparable、IFormattable、IConvertible - これはenumに近づけることができます:) - Kit

2

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;
}


1

メソッド内で元のコードを使用すると、リフレクションを使用して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;
    }
}


  • ありがとう、しかしそれはコンパイル時の問題(where制約)を実行時の問題(あなたの例外)に変えます。また、入力をintに変換する必要があります。 - Keith

1

ここで私がやったいくつかのコードは、あまりにも狂ったことをすることなくあなたが望むように動作するようです。 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.");
    }
}


0

(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&lt;Enum>Parse&lt;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));
  }

}

これが誰かを助けることを望みます


0

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());
    }

リンクされた質問


関連する質問

最近の質問