12

次の拡張メソッドがあります。

public static IFoo Foo(this IFluentApi api, Action action);

public static IFoo<TResult> Foo<TResult>(
    this IFluentApi api, Func<TResult> func);

public static IBar Bar(this IFoo foo);

public static void FooBar(this IBar bar, Action action);

public static void FooBar<TResult>( // <- this one cannot work as desired 
    this IBar bar, Action<TResult> action);

ジェネリック インターフェイスは常に、対応する非ジェネリック インターフェイスから派生します。

残念ながら、これを機能させるには:

api.Foo(x => ReturnLong())
   .Bar()
   .FooBar(x => ...); // x should be of type long

次の拡張メソッドも実装する必要があります。

public static IBar<TResult> Bar<TResult> (this IFoo<TResult> foo);

上記の拡張メソッドの最後の部分を次のように変更します。

public static void FooBar<TResult>(
    this IBar<TResult> bar, Action<TResult> action);

私は実際には とBar()の間だけFoo()FooBar()なく、メソッドの非常に長いチェーンを持っているので、莫大な追加の実装コストがかかります。

この問題を回避し、「魔法のように」TResultジェネリック パラメータを転送する方法はありますか?

編集:

型推論を失うことなく!

4

4 に答える 4

1

C# 内の流暢なインターフェイスは、各 を介して (明示的または暗黙的に) 渡される型に依存しています.。あなたが説明したように、型情報を失うと、元に戻すことはできません。

ショーンの回答で説明されているように、式に分岐を含めるか、IBar<TResult> Bar<TResult> (this IFoo<TResult> foo)必要な型情報が常に渡されるようにする必要があります。

  • もちろん、あなた.Barの のいくつかが実際に似ている.Firstか、とにかく(少なくとも直接ではなく).SingleOrDefault続くべきではない場合。.FooBar
于 2013-08-06T13:08:14.453 に答える
0

ジェネリック型はコンパイル時に認識されている必要があることに注意してください。実行時にタイプ クラスのインスタンスを格納できますが、ジェネリック パラメーターの代わりに使用することはできません。

IFooとの 2 つのタイプを作成しましIFoo<T> : IFooた。ただし、IBarクラスは、IFooホストしていないため、そのタイプに関する情報を持たない によって作成されます。したがって、型情報は失われます。コンパイル時に型を推測できるソリューションのみを検討できます。


最初の解決策はご存知です。呼び出しチェーンで使用しているすべての型のジェネリック バージョンを作成することです。それには多くの努力が必要です。


実行中に型が変更されないと仮定できる場合は、その型をラップしてメソッドを明示的に使用できます。

api.Foo(() => default(long))
   .Bar()
   .FooBar<long>(x => { });

これにより、後で汎用ラッパーを作成できます。コーディング中に型を推測できる必要があるため、これも理にかなっています。そうでない場合は、ジェネリックをまったく使用できません。


3 番目の非常に柔軟なアプローチは、単純なオブジェクトを優先してジェネリックを取り除くことです。

void FooBar(this IBar bar, Action<object> action) { /* ... */ }

.
.
.

api.Foo(() => default(long))
   .Bar()
   .FooBar(x => { }); // <-- That will compile, but x is an object

FooBar は、アクションの引数を送信する責任があることに注意してください。したがって、処理しているオブジェクトのタイプを実行時に確認できます。

.
.
.FooBar(x => { if (x is MyType) { /* ... */ } });

リフレクションによって、x に関するすべての必要な情報を取得できます。

于 2013-08-09T09:14:54.490 に答える