对于很多人来说这将是泛型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
由于
答案 0 :(得分: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);
所有这三种方法都有其优点和缺点。考虑考虑您将要使用它们的上下文。
答案 1 :(得分:0)
您可以执行以下操作:
IRecordedItemsProcessor
(非通用)Load
和Save
移至此IRecordedItemsProcessor
IRecordedItemsProcessor<T>
继承此IRecordedItemsProcessor
MyClass
期待IRecordedItemsProcessor
这清楚地表明MyClass
并不关心处理器可以解析什么类型,甚至根本不能解析事物 - 它只知道它可以保存和加载。
答案 2 :(得分: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个用于加载/保存,然后一个用于解析。在这种情况下,您可以使用继承:
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
根据您的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() );
}
}
答案 3 :(得分: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)
{
}
}