2016-03-31 12 views
1

私はIQueryable<CustomType>という読み込みモデルを持っています。私は、このモデルから別のView Modelを抽出する必要があります。私は次のように拡張メソッドを記述するために使用一般的な戻り値の型に基づいてメソッドのオーバーロードをシミュレートする方法C#

:この仕事を

public static ViewModelA AsViewModelA(this IQueryable<CustomType> query) 
{ 
    var vm = view 
       .Select(x => new ViewModelA 
       { 
        Something = x.Something 
       }).FirstOrDefault(); 

    return vm; 
} 

public static ViewModelB AsViewModelB(this IQueryable<CustomType> query) 
{ 
    var vm = view 
       .Select(x => new ViewModelB 
       { 
        SomethingElse = x.SomethingElse 
       }).FirstOrDefault(); 

    return vm; 
} 

が、私はメソッド名で生成された混乱が好きではありません。より一般的な方法は、このようなものが好ましいだろう:

query.AsViewModel<ViewModelA>() 

私は、戻り値の型は、メソッドのシグネチャとして意図されていないことを知っている(ように過負荷が適用されない)と私は、ジェネリック型の過負荷を作るのに十分ではないことを知っています。 私はただのメカニズムをシミュレートしていますジェネリック型に基づいてオーバーロードします。このメカニズムはif/then/elseをカスケードする主なメソッドを避けるべきです。やり方がある?多分ダイナミクスと?

+2

'TypeA'は' ViewModelA'ですか?実装はどのように見えるのですか? –

+0

@JonSkeet実装で修正されました –

答えて

0

まあ、はい、あなたはdynamicを使用することができます。

private static ViewModelA AsViewModelInternal(this IQueryable<CustomType> query, 
               ViewModelA dummy) { ... } 

private static ViewModelB AsViewModelInternal(this IQueryable<CustomType> query, 
               ViewModelB dummy) { ... } 

public static T AsViewModel<T>(this IQueryable<CustomType> query) 
{ 
    return (T)query.AsViewModelInternal(default(T)); 
} 

もちろん、非既存の過負荷を処理するようにしてください:)最も簡単な方法は、最後の引数としてobjectを受け取るオーバーロードを追加することで、基本的には「フォールバックオーバーロード」があります。

しかし、私はそれをお勧めしません。ジェネリックの大きなメリットの1つは、コンパイル時のチェックをうまく行うことです。この一般的な方法は、すべての可能なTを受け入れるように見せかけるが、実際にはそうではない。それはViewModelA/ViewModelBの代わりにobjectを取るのと同等です。

あなたが、多くの場合、自分が AsViewModelを呼び出すときにジェネリック型引数を使用する必要が見つけた場合、私は唯一の代替を使用したい

query.AsViewModel<ViewModelB>() 

query.AsViewModelB() 

間と

世界の違いがあります好きではないです

、あなたが特定のタイプを事前に知っていないとき。

1

1つのオプションは、タイプからCustomTypeのタイプへの変換までのマップを持つことです。あなたはより多くの辞書構築ビットとビットクリーンアップフロントのコードを作ることができ

private static readonly Dictionary<Type, Expression> Mappings = 
    new Dictionary<Type, Expression> 
    { 
     { typeof(ViewModelA), 
      Helper<ViewModelA>(x => new ViewModelA { Something = x.Something }) }, 
     { typeof(ViewModelB), 
      Helper<ViewModelB>(x => new ViewModelB { SomethingElse = x.SomethingElse }) }, 
     ... 
    } 

// This method just helps avoid casting all over the place. 
// In C# 6 you could use an expression-bodied member - or add a 
private static Expression<Func<CustomType, T>> Helper<T> 
    (Expression<Func<CustomType, T>> expression) 
{ 
    return expression; 
} 

public static T AsViewModel<T>(this IQueryable<CustomType> query) 
{ 
    Expression rawMapping; 
    if (!Mappings.TryGetValue(typeof(T), out rawMapping)) 
    { 
     throw new InvalidOperationException("Or another exception..."); 
    } 
    // This will always be valid if we've set up the dictionary properly 
    var mapping = (Expression<Func<CustomType, T>>) rawMapping; 
    return view.Select(mapping).FirstOrDefault(); 
} 

:だから、のような何かに見えるでしょう。

+0

これらのメソッドを避け、単にメソッド名を変更する必要がありますか? –

+0

@ marianoc84:それはあなた次第です。これらのうちのいくつかしか持っていない場合、複数の方法を持つことは妥当です。 *たくさんのビューモデルを持っているなら、ここでの辞書のアプローチはかなりいいですが、もちろん型引数のコンパイル時のチェックはできません。 –

関連する問題