21

メソッドがメソッドからいくつかのジェネリック型のいずれかを返すようにする方法はありますか?たとえば、私は次のようにしています。

public static T ParseAttributeValue<T>(this XElement element, string attribute)
    {
        if(typeof(T) == typeof(Int32))
        {
            return Int32.Parse(element.Attribute(attribute).Value);
        }

        if(typeof(T) == typeof(Double))
        {
            return Double.Parse(element.Attribute(attribute).Value);
        }

        if(typeof(T) == typeof(String))
        {
            return element.Attribute(attribute).Value;
        }

        if(typeof(T) == typeof(ItemLookupType))
        {
            return Enum.Parse(typeof(T), element.Attribute(attribute).Value);
        }
    }

(これは非常に迅速なモックアップにすぎません。nullチェックなどでは、実稼働コードを大幅に徹底する必要があることを認識しています...)

しかし、コンパイラーはそれを気に入らず、Int32暗黙的に変換できないと不平を言っていますT(キャストでも機能しません)。理解できます。コンパイル時にはそれが何でTあるかを知る方法はありませんが、私は事前にそれをチェックしています。とにかく私はこの仕事をすることができますか?

4

7 に答える 7

21

私は過去にこれらのタイプのジェネリックメソッドを実行しました。型推論を取得する最も簡単な方法は、一般的なコンバーター関数を提供することです。

public static T ParseAttributeValue<T>
          (this XElement element, string attribute, Func<string, T> converter)
{
  string value = element.Attribute(attribute).Value;
  if (String.IsNullOrWhiteSpace(value)) {
    return default(T);
  }

  return converter(value);
}

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

int index = element.ParseAttributeValue("index", Convert.ToInt32);
double price = element.ParseAttributeValue("price", Convert.ToDouble);

独自の関数を提供して、世界中で楽しむこともできます(匿名タイプを返すこともできます)。

ItemLookupType lookupType = element.ParseAttributeValue("lookupType",
  value => Enum.Parse(typeof(ItemLookupType), value));

var item = element.ParseAttributeValue("items",
  value => {
    List<string> items = new List<string>();
    items.AddRange(value.Split(new [] { ',' }));
    return items;
  });
于 2012-07-19T18:55:27.027 に答える
10

.Netには、使用できる優れた文字列変換ルーチンがすでにたくさんあります。ATypeConverterはあなたのために大部分の重労働を行うことができます。そうすれば、組み込み型に独自の解析実装を提供することを心配する必要はありません。

TypeConverter異なるカルチャで表現された値の解析を処理する必要がある場合に使用できる、ロケール対応バージョンのAPIがあることに注意してください。

次のコードは、デフォルトのカルチャを使用して値を解析します。

using System.ComponentModel;

public static T ParseAttributeValue<T>(this XElement element, string attribute)
{
    var converter = TypeDescriptor.GetConverter(typeof(T));
    if (converter.CanConvertFrom(typeof(string)))
    {
        string value = element.Attribute(attribute).Value;
        return (T)converter.ConvertFromString(value);
    }

    return default(T);
}

これは多くの組み込み型で機能し、カスタム型をで装飾TypeConverterAttributeして、型変換ゲームにも参加できるようにすることができます。これは、将来、の実装を変更することなく、新しい型を解析できるようになることを意味しますParseAttributeValue

参照: http: //msdn.microsoft.com/en-us/library/system.componentmodel.typeconverter.aspx

于 2012-07-19T23:03:34.180 に答える
4

なぜtypeパラメーターを戻り型として使用しているのですか?これは機能します。呼び出した後にキャストが必要です。

public static Object ParseAttributeValue<T>(this XElement element, string attribute)
{
    if(typeof(T) == typeof(Int32))
    {
        return Int32.Parse(element.Attribute(attribute).Value);
    }

    if(typeof(T) == typeof(Double))
    {
        return Double.Parse(element.Attribute(attribute).Value);
    }

    if(typeof(T) == typeof(String))
    {
        return element.Attribute(attribute).Value;
    }

    if(typeof(T) == typeof(ItemLookupType))
    {
        return Enum.Parse(typeof(T), element.Attribute(attribute).Value);
    }
}

またはさらに良い:

public static Int32 ParseAsInt32(this XElement element, string attribute)
{
    return Int32.Parse(element.Attribute(attribute).Value);
}

// etc, repeat for each type

この2番目のアプローチには、インライン化される可能性がはるかに高いという追加の利点があります。さらに、(Int32などの値型の場合)値をボックス化/ボックス化解除する必要がなくなります。これらの両方により、メソッドの実行速度がいくらか速くなります。

于 2012-07-19T18:46:46.653 に答える
2

これがまさにあなたが望むものであるかどうかはわかりませんが、object最初にキャストしてからにキャストすると、リターンを機能させることができますT

    public static T ParseAttributeValue<T>(this XElement element, string attribute)
    {
        if (typeof(T) == typeof(Int32))
        {
            return (T)(object)Int32.Parse(element.Attribute(attribute).Value);
        }

        if (typeof(T) == typeof(Double))
        {
            return (T)(object)Double.Parse(element.Attribute(attribute).Value);
        }

        if (typeof(T) == typeof(String))
        {
            return (T)(object)element.Attribute(attribute).Value;
        }

        return default(T);
    }

ただしT、コンパイル時に次のようなメソッドを呼び出して提供する必要があります。

int value = element.ParseAttributeValue<int>("attribute");
于 2012-07-19T18:55:38.803 に答える
2

これを行う2つの方法があります...

    static T ReadSetting<T>(string value)
    {
        object valueObj = null;
        if (typeof(T) == typeof(Int32))
            valueObj = Int32.Parse(value);
        return (T)valueObj;
    }
    static dynamic ReadSetting2<T>(string value)
    {
        if (typeof(T) == typeof(Int32))
            return Int32.Parse(value);
        throw new UnsupportedException("Type is unsupported");
    }
    static void Main(string[] args)
    {
        int val1 = ReadSetting<Int32>("2");
        int val2 = ReadSetting2<Int32>("3");
    }
于 2012-07-19T18:55:57.043 に答える
1

ルーチンが実行されるたびに型パラメーターをテストするのではなく、次のようなジェネリック静的クラスを作成することをお勧めします。

内部静的クラスElementParser<T>
{{
  public static Func <XElement、string、T> Convert = InitConvert;

  T DefaultConvert(XElement要素、文字列属性)
  {{
    Default(T);を返します。//または、例外をスローするなど
  }

  T InitConvert(XElement要素、文字列属性)
  {{
    if(ElementParser <int> .Convert == ElementParser <int> .InitConvert)
    {//すべてのタイプで初めてここに
      Convert = DefaultConvert; //以下のこの割り当てを上書きする可能性があります
      ElementParser <int> .Convert =
        (XElement要素、文字列属性)=>
          Int32.Parse(element.Attribute(attribute).Value);
      ElementParser <double> .Convert =
        (XElement要素、文字列属性)=>
          Int32.Parse(element.Attribute(attribute).Value);
      //他のタイプの場合など
    }
    else //他のタイプを実行しましたが、このタイプは実行していません。また、それに対して良いことは何もしていません。
    {{
      Convert = DefaultConvert;
    }
    Convert(element、attribute);を返します。      
  }
}
public static T ParseAttributeValue(このXElement要素、文字列属性)
{{
  ElementParser <T> .Convert(element、attribute);
}

このアプローチを使用すると、特定のタイプを初めて使用するときに特別な処理を行うだけで済みます。その後、単一のジェネリックデリゲート呼び出しのみを使用して変換を実行できます。かつては、任意の数のタイプを簡単に追加でき、実行時に任意のタイプのコンバーターを登録することもできます。

于 2012-07-19T23:23:02.863 に答える
1

C ++テンプレートでは、この種のことは機能しますが、コードの各部分が異なる個別の特殊化にある場合に限ります。それを機能させるのは、未使用の関数テンプレートがコンパイルされていない(より正確には:完全にインスタンス化されていない)ことです。したがって、テンプレートのコピーが別のタイプでインスタンス化された場合、コードの一部が無効になるという事実はありません。やってくる。

C#は異なり、AFAIKにはジェネリックスの専門分野はありません。C#の制限内で作業しながら、実行しようとしていることを実行する1つの方法は、より抽象的な戻り型を持つ1つの関数を作成し、ParseAttributeValueを使用してそれをTにキャストすることです。

だからあなたは持っているでしょう:

private static Object AbstractParseValue(System.Type t, XElement element, string attribute)

public static T ParseAttributeValue<T>(this XElement element, string attribute)
{
     return (T)AbstractParseValue(typeof(T), element, attribute);
}
于 2012-07-19T18:57:48.190 に答える