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 实现 a 看起来很奇怪,因为它需要做的就是调用 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);

第二种解决方案是从构造函数中删除通用输入并推断类型。然后你不需要从调用中准确指定泛型。类声明将如下所示:

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

    }
}

然后你可以简单地打电话

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

想法是您始终需要显式指定类级别的泛型,但编译器可以推断方法级别的泛型。

第三种解决方案是将创建机制封装在内部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);

所有这三种方法都有其优点和缺点。考虑考虑使用它们的上下文。

于 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

您可以执行以下操作:

  1. 创建一个新接口IRecordedItemsProcessor(非泛型)
  2. 移动LoadSave这里IRecordedItemsProcessor
  3. 从此IRecordedItemsProcessor<T>继承IRecordedItemsProcessor
  4. 在其构造函数中生成MyClass期望IRecordedItemsProcessor

这清楚地表明,MyClass它并不关心处理器可能能够解析什么类型,甚至根本不关心它可以解析事物——它只知道它可以保存和加载。

于 2012-08-22T14:59:38.663 回答
0

所写的泛型类型是在MyClass构造函数上声明的,这意味着泛型类型必须在MyClass级别定义:

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

    }
}

但是,如果泛型类型是在方法级别声明的,则只需在方法级别定义它:

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

    }
}

编辑
根据您的评论:

我想要一个可以调用加载/保存方法但不担心 T 的类。

然后你需要 2 个接口: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
}

编辑 2
根据您的要点示例,可以将类型依赖项移出接口并直接移入接口方法:

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 回答