web-dev-qa-db-ja.com

C#ジェネリックと型チェック

IList<T>をパラメーターとして使用するメソッドがあります。そのTオブジェクトのタイプを確認し、それに基づいて何かをする必要があります。 T値を使用しようとしましたが、コンパイラは許可していません。私の解決策は次のとおりです。

private static string BuildClause<T>(IList<T> clause)
{
    if (clause.Count > 0)
    {
        if (clause[0] is int || clause[0] is decimal)
        {
           //do something
        }
        else if (clause[0] is String)
        {
           //do something else
        }
        else if (...) //etc for all the types
        else
        {
           throw new ApplicationException("Invalid type");
        }
    } 
}

これを行うには、より良い方法が必要です。渡されるTのタイプを確認してからswitchステートメントを使用する方法はありますか?

64
Jon

オーバーロードを使用できます:

public static string BuildClause(List<string> l){...}

public static string BuildClause(List<int> l){...}

public static string BuildClause<T>(List<T> l){...}

または、ジェネリックパラメーターの型を検査できます。

Type listType = typeof(T);
if(listType == typeof(int)){...}
95
jonnii

typeof(T)を使用できます。

private static string BuildClause<T>(IList<T> clause)
{
     Type itemType = typeof(T);
     if(itemType == typeof(int) || itemType == typeof(decimal))
    ...
}
18
bdowden

デフォルトでは、素晴らしい方法がないことを知っています。しばらく前、私はこれにイライラし、少し助けて、構文を少しきれいにする小さなユーティリティクラスを書きました。基本的にはコードを

TypeSwitcher.Do(clause[0],
  TypeSwitch.Case<int>(x => ...),  // x is an int
  TypeSwitch.Case<decimal>(d => ...), // d is a decimal 
  TypeSwitch.Case<string>(s => ...)); // s is a string

完全なブログ投稿と実装の詳細はこちらから入手できます

6
JaredPar

Typeof演算子...

typeof(T)

... c#switchステートメントでは機能しません。しかし、これはどうですか?次の投稿には静的クラスが含まれています...

「スイッチオンタイプ」のこれよりも良い代替手段はありますか?

...次のようなコードを記述できます。

TypeSwitch.Do(
    sender,
    TypeSwitch.Case<Button>(() => textBox1.Text = "Hit a Button"),
    TypeSwitch.Case<CheckBox>(x => textBox1.Text = "Checkbox is " + x.Checked),
    TypeSwitch.Default(() => textBox1.Text = "Not sure what is hovered over"));
4
Robert Harvey

型をチェックし、型に基づいて何かを行うことはジェネリックの素晴らしいアイデアではないと言う誰にとっても、私は同意しますが、これが完全に理にかなっている状況があると思います。

たとえば、そのように実装されているクラスがある場合(注:このコードが簡単にするために行うことをすべて示しているわけではなく、ここにカットアンドペーストしているだけなので、コード全体のようにビルドまたは動作しない可能性がありますが、また、Unitは列挙型です):

public class FoodCount<TValue> : BaseFoodCount
{
    public TValue Value { get; set; }

    public override string ToString()
    {
        if (Value is decimal)
        {
            // Code not cleaned up yet
            // Some code and values defined in base class

            mstrValue = Value.ToString();
            decimal mdecValue;
            decimal.TryParse(mstrValue, out mdecValue);

            mstrValue = decimal.Round(mdecValue).ToString();

            mstrValue = mstrValue + mstrUnitOfMeasurement;
            return mstrValue;
        }
        else
        {
            // Simply return a string
            string str = Value.ToString() + mstrUnitOfMeasurement;
            return str;
        }
    }
}

...

public class SaturatedFat : FoodCountWithDailyValue<decimal>
{
    public SaturatedFat()
    {
        mUnit = Unit.g;
    }

}

public class Fiber : FoodCount<int>
{
    public Fiber()
    {
        mUnit = Unit.g;
    }
}

public void DoSomething()
{
       nutritionFields.SaturatedFat oSatFat = new nutritionFields.SaturatedFat();

       string mstrValueToDisplayPreFormatted= oSatFat.ToString();
}

要約すると、何か特別なことをするために、ジェネリックがどの型であるかを確認したい正当な理由があると思います。

2
John

やりたいことのためにswitchステートメントを使用する方法はありません。 switchステートメントには整数型を指定する必要があります。整数型には、 "Type"オブジェクトなどの複雑な型や、その他のオブジェクト型は含まれません。

2
womp

あなたの構造は、一般的なメソッドの目的を完全に無効にします。あなたが達成しようとしているものを達成するためのより良い方法が必要だからです。

2
mqp

また、C#が進化したため、(今) パターンマッチング を使用できます

private static string BuildClause<T>(IList<T> clause)
{
    if (clause.Count > 0)
    {
        switch (clause[0])
        {
            case int x: // do something with x, which is an int here...
            case decimal x: // do something with x, which is a decimal here...
            case string x: // do something with x, which is a string here...
            ...
            default: throw new ApplicationException("Invalid type");
        }
    }
}
2
Kit

typeOf(T)を実行できますが、ここでメソッドを再確認し、単一の責任に違反していないことを確認します。これはコードのにおいであり、それを行うべきではないということではなく、注意する必要があります。

ジェネリックのポイントは、型が何であるかを気にせずに、または特定の基準セットに適合する限り、型に依存しないアルゴリズムを構築できることです。実装はあまり一般的ではありません。

1
JoshBerke

これがあなたのお役に立てば幸いです。

  • typeof(IList<T>).IsGenericType == true
  • typeof(IList<T>).GetGenericTypeDefinition() == typeof(IList<>)
  • typeof(IList<int>).GetGenericArguments()[0] == typeof(int)

https://dotnetfiddle.net/5qUZnt

0
Jaider

これはどう :

            // Checks to see if the value passed is valid. 
            if (!TypeDescriptor.GetConverter(typeof(T)).IsValid(value))
            {
                throw new ArgumentException();
            }
0
Bert