1

これは多くの場合ジェネリックス101になりますが、以下はサンプルコードなので、理解を深めることができます。

public interface IRecordedItemsProcessor<T>
{
    ObservableCollection<RecordedItem> Load(string name);
    void Save();
    RecordedItem Parse(T itemToParse);
}

public class FileLoadingProcessor : IRecordedItemsProcessor<string>
{
    public ObservableCollection<RecordedItem> Load(string name)
    {
    }

    public void Save()
    {
    }

    public RecordedItem Parse(string itemToParse)
    {
    }
}

public class MyClass
{
    public MyClass(IRecordedItemsProcessor<T> processor)
    {

    }

}

問題はMyClass、依存関係が必要ですが、IRecordedItemsProcessor<T>それが何であるかを知らないため、コンパイルされないということTです。これはどのように解決できますか?MyClassを実装させるのは奇妙に思えます。必要なのは、Load/Saveを呼び出すことだけです。

ありがとう

4

4 に答える 4

5

最初の解決策は最も単純なものです。ジェネリック宣言をクラスレベルに持ち上げます。

public class MyClass<T>
{
    public MyClass(IRecordedItemsProcessor<T> processor)
    {

    }
}

MyClass次に、次のようにインスタンス化できます。

var myClass = new MyClass<string>(new FileLoadingProcessor());
Console.WriteLine (myClass);

2 番目の解決策は、コンストラクターからジェネリック入力を削除し、型を推論することです。次に、呼び出しからジェネリックを正確に指定する必要はありません。クラス宣言は次のようになります。

public class MyClass
{
    public void Process<T>(IRecordedItemsProcessor<T> processor)
    {

    }
}

そして、あなたは簡単に呼び出すことができます

var my = new MyClass();
my.Process(new FileLoadingProcessor());

アイデアは、常にクラス レベルのジェネリックを明示的に指定する必要があるということですが、メソッド レベルのジェネリックはコンパイラによって推論できます。

3 番目の解決策は、作成メカニズムを 内にカプセル化することMyClassFactoryです。IRecordedItemsProcessor<T>これは非常に柔軟ですが、 の子孫はクラス レベルでジェネリックを定義しないため、少し複雑に見えるかもしれません。そのため、実装されたインターフェイスに移動して、ジェネリック型を取得する必要があります。そうして初めて、 Generic を構築できMyClassます。リストは以下のとおりです。

public class MyClassFactory
{
    public MyClass<T> MakeMyClassFor<T>(IRecordedItemsProcessor<T> processor)
    {
        var processorGenericType = processor.GetType()
                                            .GetInterfaces()
                                            .Single(intr=>intr.Name == "IRecordedItemsProcessor`1")
                                            .GetGenericArguments()[0];
        var myClassType = typeof(MyClass<>).MakeGenericType(processorGenericType);
        return Activator.CreateInstance(myClassType, processor) as MyClass<T>;
    }
}

MyClass を非常に簡単に作成できるようになりました

 var myClassFactory = new MyClassFactory();
 var res = myClassFactory.MakeMyClassFor(new FileLoadingProcessor());
 Console.WriteLine (res);

これら 3 つのアプローチにはすべて、長所と短所があります。それらを使用するコンテキストを考慮に入れることを検討してください。

于 2012-08-22T14:43:43.550 に答える
0

非ジェネリック マーカー インターフェイスから継承できます。これにより、クラスで T について知る必要がなくなります。

public interface IRecordedItemsProcessor 
{
}

public interface IRecordedItemsProcessor<T> : IRecordedItemsProcessor
{
    ObservableCollection<RecordedItem> Load(string name);
    void Save();
    RecordedItem Parse(T itemToParse);
}

そして、次のような IRecordedItemsProcessor を使用できます。

public class MyClass
{
    public MyClass(IRecordedItemsProcessor processor)
    {

    }

}
于 2012-08-22T15:34:59.670 に答える
0

書かれているように、ジェネリック型はコンストラクターで宣言されています。これは、ジェネリック型をレベルMyClassで定義する必要があることを意味します。MyClass

public class MyClass<T>
{
    public MyClass(IRecordedItemsProcessor<T> processor)
    {

    }
}

ただし、ジェネリック型がメソッド レベルで宣言されている場合は、メソッド レベルでのみ定義する必要があります。

public class MyClass
{
    public void MyMethod<T>( IRecordedItemsProcessor<T> processor )
    {

    }
}

編集
あなたのコメントに基づいて:

Load/Save メソッドを呼び出すことができるが、T がそうであることを心配しないクラスが必要です。

次に、2 つのインターフェイスが必要になります。1 つは読み込み/保存用で、もう 1 つは解析用です。この場合、継承を使用できます。

public interface IRecordedItems
{
    ObservableCollection<RecordedItem> Load( string name );
    void Save();
}


public interface IRecordedItemsProcessor<T> : IRecordedItems
{
    RecordedItem Parse( T itemToParse );
}

public class MyClass : IRecordedItems
{
    #region Implementation of IRecordedItems

    public ObservableCollection<RecordedItem> Load( string name )
    {
        throw new NotImplementedException();
    }

    public void Save()
    {
        throw new NotImplementedException();
    }

    #endregion
}

EDIT 2 Gist example
に 基づいて、型の依存関係をインターフェースから直接インターフェースメソッドに移動できます。

public class RecordedItem {}

public interface IRecordedItemsProcessor
{
    ObservableCollection<RecordedItem> Load( string name );

    void Save();

    RecordedItem Parse<T>( T itemToParse );
}

public class MyClass
{
    private readonly IRecordedItemsProcessor _processor;

    public MyClass( IRecordedItemsProcessor processor )
    {
        _processor = processor;

        processor.Parse<string>( "foo" );
        processor.Parse<int>( 10 );
        processor.Parse<RecordedItem>( new RecordedItem() );
    }
}
于 2012-08-22T15:00:58.257 に答える
0

次のことができます。

  1. 新しいインターフェースを作成するIRecordedItemsProcessor(非ジェネリック)
  2. 移動LoadSaveてこちらへIRecordedItemsProcessor
  3. これからIRecordedItemsProcessor<T>継承するIRecordedItemsProcessor
  4. そのコンストラクターで期待MyClassするIRecordedItemsProcessor

これによりMyClass、プロセッサがどのタイプを解析できるか、またはまったく解析できることさえ気にしないことが明らかになります。

于 2012-08-22T14:59:38.663 に答える