IoCクラスライブラリの流暢なインターフェイスの書き直しに取り組んでいます。基本クラスを通じていくつかの共通機能を共有するためにコードをリファクタリングしたときに、問題が発生しました。
注:これは私がやりたいことであり、私がしなければならないことではありません。別の構文でやらなければならない場合はそうしますが、コードを希望どおりにコンパイルする方法について誰かが考えている場合は、大歓迎です。
特定の基本クラスでいくつかの拡張メソッドを使用できるようにしたいのですが、これらのメソッドは、メソッドの引数に関連する1つのジェネリック型を持つジェネリックである必要がありますが、メソッドは、特定の子孫に関連する特定の型も返す必要があります。 'が呼び出されます。
上記の説明よりもコード例の方が優れています。
動作しないものの簡単で完全な例を次に示します。
using System;
namespace ConsoleApplication16
{
public class ParameterizedRegistrationBase { }
public class ConcreteTypeRegistration : ParameterizedRegistrationBase
{
public void SomethingConcrete() { }
}
public class DelegateRegistration : ParameterizedRegistrationBase
{
public void SomethingDelegated() { }
}
public static class Extensions
{
public static ParameterizedRegistrationBase Parameter<T>(
this ParameterizedRegistrationBase p, string name, T value)
{
return p;
}
}
class Program
{
static void Main(string[] args)
{
ConcreteTypeRegistration ct = new ConcreteTypeRegistration();
ct
.Parameter<int>("age", 20)
.SomethingConcrete(); // <-- this is not available
DelegateRegistration del = new DelegateRegistration();
del
.Parameter<int>("age", 20)
.SomethingDelegated(); // <-- neither is this
}
}
}
これをコンパイルすると、次のようになります。
'ConsoleApplication16.ParameterizedRegistrationBase' does not contain a definition for 'SomethingConcrete' and no extension method 'SomethingConcrete'...
'ConsoleApplication16.ParameterizedRegistrationBase' does not contain a definition for 'SomethingDelegated' and no extension method 'SomethingDelegated'...
私が欲しいのは、拡張メソッド( )がとのParameter<T>
両方で呼び出せるようにすることです。どちらの場合も、戻り型は拡張が呼び出された型と一致する必要があります。ConcreteTypeRegistration
DelegateRegistration
問題は次のとおりです。
私は書きたいです:
ct.Parameter<string>("name", "Lasse")
^------^
notice only one generic argument
しかし、Parameter<T>
それはそれが呼び出されたのと同じタイプのオブジェクトを返します。つまり、次のことを意味します。
ct.Parameter<string>("name", "Lasse").SomethingConcrete();
^ ^-------+-------^
| |
+---------------------------------------------+
.SomethingConcrete comes from the object in "ct"
which in this case is of type ConcreteTypeRegistration
コンパイラをだましてこの飛躍をさせる方法はありますか?
メソッドに2つのジェネリック型引数を追加するとParameter
、型推論によって両方を提供するか、何も提供しないように強制されます。これは、次のことを意味します。
public static TReg Parameter<TReg, T>(
this TReg p, string name, T value)
where TReg : ParameterizedRegistrationBase
私にこれを与えます:
Using the generic method 'ConsoleApplication16.Extensions.Parameter<TReg,T>(TReg, string, T)' requires 2 type arguments
Using the generic method 'ConsoleApplication16.Extensions.Parameter<TReg,T>(TReg, string, T)' requires 2 type arguments
これも同様に悪いことです。
クラスを簡単に再構築したり、メソッドを階層に導入することで非拡張メソッドにすることもできますが、私の質問は、2つの子孫のメソッドを複製する必要がなく、何らかの方法で1回だけ宣言できるかどうかです。 、基本クラスの場合。
言い換えれば。上記の最初のコード例のクラスを変更して、問題のメソッドを複製せずにMainメソッドの構文を保持できるようにする方法はありますか?
コードは、C#3.0と4.0の両方と互換性がある必要があります。
編集:両方のジェネリック型引数を推論に残したくない理由は、一部のサービスでは、1つの型のコンストラクターパラメーターにパラメーター値を指定し、子孫である値を渡すためです。今のところ、指定された引数値と呼び出す正しいコンストラクターの照合は、引数の名前とタイプの両方を使用して行われます。
例を挙げましょう:
ServiceContainerBuilder.Register<ISomeService>(r => r
.From(f => f.ConcreteType<FileService>(ct => ct
.Parameter<Stream>("source", new FileStream(...)))));
^--+---^ ^---+----^
| |
| +- has to be a descendant of Stream
|
+- has to match constructor of FileService
両方を型推論に任せると、パラメーターの型はでFileStream
はなく、になりますStream
。