2012-07-19 11 views
20

メソッドがメソッドからいくつかのジェネリック型を返す方法はありますか?例えば、私は次のようしている:C#の変数汎用戻り型

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が何であるかを知る方法がありませんが、私は事前にチェックしています。とにかく私はこの仕事をすることができますか?

+0

いいえ、 ''はタイプパラメータのみです。ブラケットを使用しないタイプを使用します。 – Femaref

+0

@Femaref - 良い点。質問は取り下げられた。 –

+0

値をObjectとして格納すると、その値をキャストできます。また、関数を動的にタイプすると、キャストなしでそのようにすることもできます。 – MrWednesday

答えて

20

私は過去にこれらのタイプの一般的な方法を行ってきました。型推論を得る最も簡単な方法は、一般的な変換関数を提供することです。

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; 
    }); 
+0

私はそれがもっとエレガントになるとは思わない。 +1。 – Adam

+0

これは、 'Tunc'が' Func <,> 'の内部にあるので、使い方から推測できることが良い点です。したがって、余分なパラメータ(つまり、 'Func <,>'デリゲート)を提供しなければならない場合でも、型パラメータ 'T'を明示的に指定する必要はありません。 –

+1

.Netフレームワークにはたくさんの汎用コンバータが既に存在します: 'TypeDescriptor.GetTypeConverter(typeof(T));' –

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 

この第二のアプローチは、インライン化ばかりの非常に高い可能性を持つという付加的な利点を持って、プラスそれは(意志これは単なる呼び出した後にキャストを必要とし、働くだろうInt32のような値型の場合)、値をボックス化/ボックス化解除する必要がありません。これらの両方によって、メソッドがやや高速で実行されます。

+0

あなたの2番目の提案に+1。言語がすでにオーバーロードされたメソッドを提供している場合には、リフレクションを使用して型を把握し、対応するアクションを実行する必要はありません。 –

+0

はい、ジェネリックスを放棄して、明示的に名前を付けたメソッドを作成することは、これに共通する解決策であり、それほど悪くはありません。 'ParseAttributeValueAsString'とタイプするのは、Intellisenseの有無にかかわらず、' ParseAttributeValue 'と同じくらい簡単です。そしてジェネリックメソッドは既に特別なケースがたくさんあったので、別のメソッドに引き出すと重複は起こりません。 –

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"); 
+0

これはすぐ解決です。型パラメータを 'object'に明示的にキャストすることは常に許可されています。そして、' object'は何にでもキャストできます。しかし、それは醜いです。 'Int32'や' Double'などの構造体の場合はボクシングとアンボクシングが必要です。 –

2

はここでそれを行うの二つの方法... C++テンプレートで

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"); 
    } 
+1

'ReadSetting'はジェネリック型が値型であれば' NullReferenceException'をスローします。なぜなら値の型はnullにできないからです。だからこそ、「オブジェクト」に囲むことは危険です。 – Joshua

+0

これは本当ですが、多くの場合、適切な結果になる可能性があります。たとえば、メソッドを表示するために書かれた単純なサンプルコードではないと思われる場合、呼び出し元は、解析できない、または指定されていない設定を読み込もうとすると、例外を予期する可能性があります。 – MrWednesday

1

、このですある種のものはうまくいくだろうが、コードの各部分が異なる、別個の専門化にあった場合に限って。この作業を行うのは、未使用の関数テンプレートがコンパイルされない(またはより正確には完全にインスタンス化されない)ことです。そのため、テンプレートのコピーが別のタイプでインスタンス化された場合、やってくる。

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

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

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

.NETは、すでにあなたが使用することができます偉大な文字列変換ルーチンの束を持っています! TypeConverterは、あなたのために大々的に行うことができます。次に、組み込み型のための独自の解析実装を提供することを心配する必要はありません。

TypeConverterには、さまざまな文化で表現された解析値を処理する必要がある場合に使用できるAPIのロケール対応バージョンがあることに注意してください。これは、組み込み型の多くのために働くだろう、とあなたは彼らが参加できるようにするためにTypeConverterAttributeでカスタムタイプを飾ることができます

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

次のコードは、既定のカルチャを使用して値を解析しますタイプ変換ゲームもあります。つまり、将来的にはParseAttributeValueの実装を変更することなく新しいタイプを解析することができます。

参照:私はそれではなく、型パラメータにルーチンが実行されるたびにテストをお勧めしhttp://msdn.microsoft.com/en-us/library/system.componentmodel.typeconverter.aspx

0

、あなたは一般的な静的クラスを作成する必要があり、このような何か:このアプローチを使用して

 
internal static class ElementParser<T> 
{ 
    public static Func<XElement, string, T> Convert = InitConvert; 

    T DefaultConvert(XElement element, string attribute) 
    { 
    return Default(T); // Or maybe throw exception, or whatever 
    } 

    T InitConvert(XElement element, string attribute) 
    { 
    if (ElementParser<int>.Convert == ElementParser<int>.InitConvert) 
    { // First time here for any type at all 
     Convert = DefaultConvert; // May overwrite this assignment below 
     ElementParser<int>.Convert = 
     (XElement element, string attribute) => 
      Int32.Parse(element.Attribute(attribute).Value); 
     ElementParser<double>.Convert = 
     (XElement element, string attribute) => 
      Int32.Parse(element.Attribute(attribute).Value); 
     // etc. for other types 
    } 
    else // We've done other types, but not this type, and we don't do anything nice for it 
    { 
     Convert = DefaultConvert; 
    } 
    return Convert(element, attribute);  
    } 
} 
public static T ParseAttributeValue(this XElement element, string attribute) 
{ 
    ElementParser<T>.Convert(element, attribute); 
} 

、特定のタイプが初めて使用されたときに特別な処理を行うだけで済みます。その後、1回の汎用代行呼び出しを使用して変換を実行できます。いったん任意の数の型を簡単に追加でき、実行時に任意のタイプのコンバータを登録することもできます。

+0

組み込みの 'TypeConverter'クラスを使うのはなぜですか? –

+0

その特定のクラスが自分のアプリケーションに適切に機能する場合、素晴らしいです。そのクラスがサポートしていない特定の振る舞いを必要とする場合は、上記のアプローチは任意の追加の翻訳に容易に対応できます。 – supercat

関連する問題