92

私がやりたいのは次のようなものです。フラグ付きの値を組み合わせた列挙型があります。

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#は列挙型を構造体として認識しないため(値型であっても)、このような拡張型を追加することはできません。

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

4

12 に答える 12

49

編集: これは UnconstrainedMelody のバージョン 0.0.0.2 で有効になりました。

(列挙型制約に関する私のブログ投稿で要求されたとおり。スタンドアロンの回答のために、以下の基本的な事実を含めました。)

最善の解決策は、 UnconstrainedMelody 1に含めるのを待つことです。これは、次のような「偽の」制約を持つ C# コードを使用するライブラリです。

where T : struct, IEnumConstraint

そしてそれをに変えます

where T : struct, System.Enum

ビルド後のステップを介して。

書くのはそれほど難しいことではないはずIsSetです... ただし、 -based フラグInt64UInt64-based フラグの両方に対応するのは難しい部分になる可能性があります。(いくつかのヘルパーメソッドが登場するにおいがするので、基本的に、フラグ列挙型をベースタイプがあるかのように扱うことができますUInt64。)

電話をかけた場合の動作をどうしたいか

tester.IsSet(MyFlags.A | MyFlags.C)

? 指定されたすべてのフラグが設定されていることを確認する必要がありますか? それが私の期待です。

私は今夜​​家に帰る途中でこれをやろうとします...私はライブラリをすぐに使用可能な標準にするために便利な列挙型メソッドについて簡単な電撃をしたいと思っています。それから少しリラックスしてください。

IsSet編集:ちなみに、名前としてはわかりません。オプション:

  • 含む
  • 含む
  • HasFlag (または HasFlags)
  • IsSet (確かにオプションです)

考えは大歓迎です。いずれにせよ、何かが固まるまでにはしばらく時間がかかると確信しています...


もちろん、 1またはパッチとして送信します...

于 2009-09-11T09:12:34.137 に答える
16

ダレン、タイプが特定の列挙である場合は機能します。一般的な列挙が機能するには、ブール計算を行うためにそれらをint(またはより可能性が高いuint)にキャストする必要があります。

public static bool IsSet( this Enum input, Enum matchTo )
{
    return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0;
}
于 2008-08-10T22:53:05.613 に答える
10

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

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>、プライベート コンストラクターとパブリックのネストされた抽象継承クラスをTempasで指定できます。Enum

于 2009-09-13T02:41:25.597 に答える
8

これは、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
    {
    }
}
于 2012-07-20T07:11:35.043 に答える
4

これは元の質問には答えませんが、.NET 4 にはEnum.HasFlagと呼ばれるメソッドがあり、例でやろうとしていることを実行します

于 2009-11-20T11:08:24.343 に答える
3

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

于 2009-07-27T14:02:01.490 に答える
1

これが私が作成したばかりのコードで、あまりにもクレイジーなことをしなくても、あなたが望むように機能するようです。フラグとして設定された列挙型だけに制限されているわけではありませんが、必要に応じて常にチェックを入れることができます。

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.");
    }
}
于 2009-09-13T03:57:16.527 に答える
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;
    }
}
于 2008-08-17T04:51:38.663 に答える
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());
    }
于 2016-03-18T07:49:08.703 に答える
0

誰かが一般的な IsSet を必要とする場合 (オンザフライで作成されたものは改善される可能性があります)、または文字列から Enum のみの変換 (以下に示す 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&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));
  }

}

これが誰かに役立つことを願っています。

于 2016-12-22T08:42:06.460 に答える