3

これが私が持っているものの一種です(Rhino Mocksを使用していますが、それは質問の中心ではありません):

var entityMock = MockRepository.GenerateMock<IEntity>();
this.Cache = MockRepository.GenerateStub<Cache<IEntity>>();

タイプパラメータの設定をより具体的にすることは可能Cache<T>ですか?何かのようなもの:

var entityMock = MockRepository.GenerateMock<IEntity>();
this.Cache = MockRepository.GenerateStub<Cache<typeof(entityMock)>>();

もちろん、これはコンパイルされません。ただし、可能であれば、Rhino Mocksが生成するタイプを使用したいと思います。これは、の具体的な実装ですIEntity

4

3 に答える 3

3

リフレクションを使用して、実行時にクローズ ジェネリック型を作成できます。問題は、リフレクションだけを使用して操作を続行する必要がある可能性が高いことです (コンパイル時にその型が不明であるため) 直接使用できるものとして型付けすることはできません。

たとえば、「何か」のリストを作成するには:

public IList CreateList(Type t)
{
    var openListType = typeof(List<>);
    return (IList)openListType.MakeGenericType(t);
}

この例は、いくつかの重要な点を示しています。

  1. コンパイル時に「ターゲット タイプ」が指定されている場合、これを行うことはできません。つまり、ジェネリック型パラメーターとしてCreateList受け入れることはできずt、同じ機能を引き続き使用できます。
  2. 最悪の場合、新しいインスタンスはobject. ここでは、常に を作成することがわかっているIListので、状況は少し良くなります。

Rhino Mocks の経験はありませんが、あなたの場合は次のようになります。

var entityMock = MockRepository.GenerateMock<IEntity>()
var cacheType = typeof(Cache<>).MakeGenericType(entityMock.GetType());
this.Cache = MockRepository.GenerateStub(cacheType);

...ただし、適切な過負荷が利用可能な場合のみGenerateStub

于 2012-07-11T08:28:16.663 に答える
2

RhinoMocksについて言及されていますが、特定の部分は、提供されたタイプの周りに具体的な実装を作成することだけであることに注意してください。GenerateStub残りはRhinoMocksにとらわれません。

ご存知のように、ジェネリック型の引数はコンパイル時に解決されます。これにより、リフレクションなしでインラインで一般的な引数として型を入力することはできません(例:) var list = new List<typeof(int)>();

ただし、リフレクションを使用してジェネリック型を作成できます。本質的に、動的プロキシのタイプを次のようなものから取得できる場合:

var entityMock = MockRepository.GenerateMock<IEntity>();
var dynamicType = entityMock.GetType();

MockRepositoryと引数GenerateStubを取るaがあるので、上から続けます。Typeobject[]

var cacheType = typeof(Cache<>);
var genericType = cacheType.MakeGenericType(dynamicType);
var stubbed = MockRepository.GenerateStub(genericType, null);

stubbedアイテムは残念ながらタイプですが、objectLuceroがコメントで述べているように、一般的な共分散を使用して、単なるよりも使いやすいタイプを取得することができますobject。これを以下に示します。


Luceroとの興味深い議論に基づいてICache<out T>、キャッシュを表す新しいインターフェイスを定義すると、一般的な共分散により、結果のプロキシを(のICache<IEntity>)ベースタイプにキャストできるようになります。

class Program
{
    static void Main(string[] args)
    {
        // The concrete entity.
        IEntity entityMock = MockRepository.GenerateMock<IEntity>();
        entityMock.Stub(s => s.Name).Return("Adam");

        // The runtime type of the entity, this'll be typeof(a RhinoMocks proxy).
        Type dynamicType = entityMock.GetType();

        // Our open generic type.
        Type cacheType = typeof(ICache<>);

        // Get the generic type of ICache<DynamicProxyForIEntity> (our closed generic type).
        Type  genericType = cacheType.MakeGenericType(dynamicType);

        // Concrete instance of ICache<DynamicProxyForIEntity>.
        object stubbed = MockRepository.GenerateStub(genericType, null);

        // Because of the generic co-variance in ICache<out T>, we can cast our
        // dynamic concrete implementation down to a base representation
        // (hint: try removing <out T> for <T> and it will compile, but not run).
        ICache<IEntity> typedStub = (ICache<IEntity>)stubbed;

        // Stub our interface with our concrete entity.
        typedStub.Stub(s => s.Item).Return(entityMock);

        Console.WriteLine(typedStub.Item.Name); // Prints "Adam".
        Console.ReadLine();
    }
}

public interface ICache<out T>
{
    T Item { get; }
}

public interface IEntity
{
    string Name { get; }
}
于 2012-07-11T08:24:42.357 に答える
0

ジェネリック型が特定の制約を満たすかどうかに基づいてコードが異なるアクションを実行したい場合、特にジェネリック型が満たさない場合にこれらのアクションの一部をコンパイルすることさえできない場合は、Comparer<T>や などのクラスで使用されますEqualityComparer<T>T秘訣は、 type のパラメーターを持つメソッドへの静的デリゲートを保持するジェネリック パラメーターを持つ静的クラスを使用し、静的メソッドを持つことTです。それぞれのメソッドは、制約付きの可能性のある型のジェネリック パラメーターを取り、次のようなUシグネチャを持ちます。Uマッチ時に前述のデリゲートと互換性があるT. 静的クラスまたはそのデリゲートを初めて使用しようとすると、ジェネリック クラスの静的コンストラクターはリフレクションを使用して、型に使用する必要があるメソッドへのデリゲートを構築できます。このような呼び出しは、ジェネリック メソッドのジェネリック制約に違反するような方法でジェネリック メソッドへのデリゲートを構築しようとすると、実行時に失敗します。静的コンストラクターの例外は非常に悪いため、有効な関数を使用してデリゲートを構築するようにしてください。一方、デリゲートが一度構築されると、以降のすべての呼び出しは、そのデリゲートを介して直接ディスパッチできます。それ以上のリフレクションや型チェックは必要ありません。

于 2012-07-11T15:39:33.197 に答える