web-dev-qa-db-ja.com

列挙値を使用または返そうとしたときに、列挙値をキャストしないようにできますか?

次の列挙型がある場合:

public enum ReturnValue{
    Success = 0,
    FailReason1 = 1,
    FailReason2 = 2
    //Etc...
}

次のように、戻ったときにキャストを回避できますか?

public static int main(string[] args){
    return (int)ReturnValue.Success;
}

そうでない場合、列挙値がデフォルトでintとして扱われないのはなぜですか?

28
Mark Carpenter

列挙型はタイプセーフであると想定されています。他の用途を思いとどまらせるために暗黙的にキャスト可能にしたわけではないと思います。フレームワークでは定数値を割り当てることができますが、意図を再検討する必要があります。主に定数値を格納するために列挙型を使用する場合は、静的クラスの使用を検討してください。

public static class ReturnValue
{
    public const int Success = 0;
    public const int FailReason1 = 1;
    public const int FailReason2 = 2;
    //Etc...
}

それはあなたがこれを行うことを可能にします。

public static int main(string[] args){
    return ReturnValue.Success;
}

[〜#〜]編集[〜#〜]

doが列挙型に値を提供したい場合は、それらを組み合わせたい場合です。以下の例を参照してください。

[Flags] // indicates bitwise operations occur on this enum
public enum DaysOfWeek : byte // byte type to limit size
{
    Sunday = 1,
    Monday = 2,
    Tuesday = 4,
    Wednesday = 8,
    Thursday = 16,
    Friday = 32,
    Saturday = 64,
    Weekend = Sunday | Saturday,
    Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday
}

この列挙型は、ビット単位の計算を使用して使用できます。一部のアプリケーションについては、以下の例を参照してください。

public static class DaysOfWeekEvaluator
{
    public static bool IsWeekends(DaysOfWeek days)
    {
        return (days & DaysOfWeek.Weekend) == DaysOfWeek.Weekend;
    }

    public static bool IsAllWeekdays(DaysOfWeek days)
    {
        return (days & DaysOfWeek.Weekdays) == DaysOfWeek.Weekdays;
    }

    public static bool HasWeekdays(DaysOfWeek days)
    {
        return ((int) (days & DaysOfWeek.Weekdays)) > 0;
    }

    public static bool HasWeekendDays(DaysOfWeek days)
    {
        return ((int) (days & DaysOfWeek.Weekend)) > 0;
    }
}
49
Michael Meadows

C#列挙型は役に立ちません。

封印されたクラスを作成し、暗黙的/明示的な変換演算子を提供することで、型からのキャストを回避し、型に明示的にキャストできる値を制約できます。

  • キャストする必要がないように、型から汎用intに変換するための暗黙的な演算子を提供します。
  • Intからtypeに変換するための明示的な演算子を提供します。これは、整数が制約を満たさない場合にエラーをスローします((int x)=>(x> = 0 && x <= 2)など)。

この手法を使用する場合は、_ConstrainedNumber<T>_などの汎用の不変の基本クラスを作成します。このクラスには、T値を受け入れ、制約を委任するコンストラクターdelegate bool NumberConstraint<T>(T value)があります。コンストラクターは、制約デリゲートを介して値を実行し、制約を満たさない場合は例外をスローする必要があります。基本クラスは、Tへの暗黙的な変換操作も処理する必要があり、object.Equals(object)およびobject.GetHashCode()をオーバーロードして、型_ConstrainedNumber<T>_の==および!=演算子を定義することによって等価性を処理する必要があります。 _IEquatable<T>_と_IEquatable<ConstrainedNumber<T>>_を実装します。また、基本クラスとすべての派生型のコピーコンストラクターを定義することをお勧めします。クローン作成は、リフレクションを介してコピーコンストラクターを取得することにより、基本クラスでクリーンに実装できますが、これは完全にオプションです。 _ConstrainedNumber<T>_の実装は、stackoverflowのどこかに投稿していない限り、自分で理解できます。

派生したConstrainedNumberに名前付きの静的読み取り専用値を指定して、列挙型のようにそれらにアクセスできるようにすることができます。

_public sealed class ReturnValue: ConstrainedNumber<int>
{
    public static readonly NumberConstraint<int> constraint = (int x) => (x >= 0 && x < 3);

    public static readonly ReturnValue Success = new ReturnValue(0);
    public static readonly ReturnValue FailReason1 = new ReturnValue(1);
    public static readonly ReturnValue FailReason2 = new ReturnValue(2);

    private ReturnValue( int value ): base( value, constraint ) {}
    private ReturnValue( ReturnValue original ): base (original) {} //may be used to support IClonable implementation in base class
    public static explicit operator ReturnValue( int value )
    {
        switch(value) //switching to return an existing instance is more efficient than creating a new one and re-checking the constraint when there is a limited number of allowed values; if the constraint was more generic, such as an even number, then you would instead return a new instance here, and make your constructors public.
        {
            case 0: return Success;
            case 1: return FailReason1;
            case 2: return FailReason2;
        }
        throw new ArgumentException( "Value fails to meet the constraint defined for " + typeof(ReturnValue).FullName + ".", "value" );
    }

}
_

この手法は、任意の制約に使用できます。たとえば、EvenNumberというクラスには、指定された数値が偶数の場合にtrueを返す制約がある場合があります。その場合は、コンストラクターをパブリックにし、静的変換演算子を単純化して、制限された既存のインスタンスの1つを返すように切り替えるのではなく、新しいEvenNumberを返すだけにします。

これは次のように使用できます。

_EvenNumber x = (EvenNumber)2;
EvenNumber y = (EvenNumber)3; //throws exception "Value fails to meet the constraint defined for {namespace}.EvenNumber."  A c# enum would stupidly allow such a cast, creating an invalid EvenNumber, breaking the object-oriented model
int z = x; //implicit conversion, no cast necessary;
_
3
Triynko

列挙型は基になる型としてintを使用する必要がないため、暗黙的なキャストはありません。たとえば、列挙型が基になる型としてuintを使用した場合、uintからintへの暗黙的なキャストはありません。

3
tvanfosson

列挙型と整数は、仕様に従って暗黙的にキャストできません(比較テスト/割り当てなどで許可されるリテラル0を除く)。ただし、必要なのは明示的なキャストだけです。

2
Marc Gravell

いいえ、キャストは避けられません。暗黙の変換がない理由についてはわかりませんが、ありません。

0
mqp

不思議なことに、これは.NET Frameworkに固有のものではなく、C#に固有のものです。他のコメント提供者がすでに指摘しているように、C#ではこれは基本的に言語の仕様です。同じことはVB.NETには当てはまりません。

VB.NETの列挙型 のMSDNリファレンスページを確認してください。列挙型の宣言時に列挙型のデータ型を指定できることに注意してください。

つまり、本当にコードに(int)へのキャストを散らかしたくない場合は、VB.NETで列挙型を記述して宣言することができます。整数として使用し、C#の列挙型を使用します。

コンピューターが私たちの生活をとてもシンプルにするだろうと彼らが私たちに言ったことを覚えていますか? :)

0
Mike

この動作は、列挙型の作成の背後にある基本的な意図に帰することができます...基になる型に応じて、指定された(またはデフォルトの)値のみを持つことができる名前付き定数のセットを作成します。

あなたの質問に関連して、考慮すべき2つの別々の問題があります:

  1. Enum値は、デフォルトではintとして扱うことができません。これは、any integerを指定でき、指定された整数が実際に機能することを検証するためのコンパイル時チェックがないためです。列挙に値として存在します。

  2. 支配型(System.Enumクラスから派生する型YourCustomEnum)から基になる型、つまりintまたはbyteなど。

0
Cerebrus