2012-08-22 23 views
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的实现似乎很奇怪,因为它需要做的就是调用加载/保存

感谢

回答

5

首先解决是最简单的一个:提升通用声明一流水平,像

然后你可以实例MyClass如下:

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

硒cond解决方案是从构造函数和推断类型中删除通用输入。那么你不需要从调用中完全指定泛型。类声明的样子:

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

    } 
} 

然后你就可以调用简单

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

的想法是,你总是需要明确指定类级别的仿制药,但方法水平的仿制药可以被推断编译器。

第三个解决方案是在MyClassFactory内封装创建机制。这很灵活,但看起来有点复杂,因为IRecordedItemsProcessor<T>的后代没有在类级别定义泛型,所以我们应该去实现的接口并获取泛型类型。只有那样我们才能构建通用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>; 
    } 
} 

现在你可以非常简单地

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

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

+0

我不想这样做。为什么MyClass上有一个?我希望MyClass与IRecordedItemsProcessor一起工作 – Jon

+0

您的评论表明,如果您的界面的某些用户不关心其某些方法,那么您的界面应该分成两个界面。 – AakashM

+0

但是,你会想要使用依赖注入来处理IRecordedItemsProcessor,那么如何使用方法工作。我只见过DI与构造函数/属性一起工作。 – Jon

0

你可以做到以下几点:从这个IRecordedItemsProcessor

  1. 创建一个新的接口IRecordedItemsProcessor(非通用)
  2. 移动LoadSave这个IRecordedItemsProcessor
  3. IRecordedItemsProcessor<T>继承
  4. MyClass预计在其构造函数IRecordedItemsProcessor

这表明MyClass并不在乎处理器能够解析什么类型,甚至不能解析事情 - 它只知道它可以保存和加载。

+0

这将工作和解析可以保持公开只是单元测试。我的班级不必调用Parse – Jon

0

通用类型,写入,被宣布对MyClass构造,这意味着一般类型必须在MyClass级别来定义:

然而,如果所述类型在一个方法级声明,那就只有在方法层面上定义:

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 
} 

编辑根据您的gist example,这种类型依赖可能会关闭接口并直接进入界面方法移动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()); 
    } 
} 
+0

IRecordedItemsProcessor 将如何注入该方法? – Jon

+0

在考虑使用DI之前,请考虑如何在具体示例中使用泛型。你是否写过一份工作样本?如果用法是'IRecordedItemsProcessor '必须是构造函数参数,则'T'必须在类级别定义:'MyClass '。 DI不会向该方法注入任何东西,因为DI不用于执行方法。相反,'IRecordedItemsProcessor '可以在属性级注入,但是再一次,类必须像构造器示例中那样定义'T'。 –

+0

我明白,它只是泛型我遇到了问题。我想要一个可以调用Load/Save方法但不担心T的类。Whereever T被定义,它似乎必须将行传递给任何接触它的MyClass中我不想做的事。 – Jon

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) 
    { 

    } 

} 
+0

这是很好的解决方案,但是如果你需要访问构造函数中的一般类型处理器呢? –

+0

@IlyaIvanov由于在构造函数的示例中没有代码,我假定引用刚刚被保存。它可以稍后使用通用方法进行投射,从而避免需要将类本身作为通用类。但是,对于答案中的方法+1,这只是另一种方式。 – dmck

+0

是的,你的解决方案适用于相当多的场合。我敢打赌,你知道,简单是软件开发的关键之一;) –