2013-07-30 153 views
8

上下文:.NET 4.0,C#C#使用泛型和接口实现

我创建了一组接口和一组实现它们来提供某些服务的接口。客户端使用具体类,但调用使用接口声明的方法作为参数类型。

一个简单的例子是这样的一个:

namespace TestGenerics 
{ 
    // Interface, of fields 
    interface IField 
    { 
    } 

    // Interface: Forms (contains fields) 
    interface IForm<T> where T : IField 
    { 

    } 

    // CONCRETE CLASES 
    class Field : IField 
    { 
    } 

    class Form <T> : IForm<T> where T : IField 
    { 
    } 

    // TEST PROGRAM 
    class Program 
    { 
     // THIS IS THE SIGNATURE OF THE METHOD I WANT TO CALL 
     // parameters are causing the error. 
     public static void TestMethod(IForm<IField> form) 
     { 
      int i = 1; 
      i = i * 5; 
     } 

     static void Main(string[] args) 
     { 
      Form<Field> b = new Form<Field>(); 
      Program.TestMethod(b); 
     } 
    } 
} 

的代码对我来说很有意义,但我得到的编译器错误:

Argument 1: cannot convert from ' TestGenerics.Form<TestGenerics.Field> ' to ' TestGenerics.IForm<TestGenerics.IField> ' TestGenerics

我不知道我做错了什么,我在网上看了很多页面,但没有解决我的问题。

是否有不会改变什么,我试图建立,大部分的架构的解决方案:

编辑:我设计的接口的方式,使得他们应该独立于实现它们的具体clases的。具体的clases可以从DLL加载,但大多数应用程序与接口一起工作。在某些情况下,我需要使用具体的clases,特别是在使用需要序列化的clases时。

在此先感谢。

亚历

+5

这会在协方差的境界之下。具体来说,如果将['out' generic modifier](http://msdn.microsoft.com/zh-cn/library/dd469487.aspx)添加到您的签名'interface IForm where T:IField',那么它将工作。但是这增加了其他限制/考虑因素,所以我不能评论它是否适用于您当前的设计/使用。 –

+2

我很好奇你在用这个设计来完成什么。 – sidesinger

+0

非常感谢这两个肛门,他们真的很好。我添加了一个编辑,解释为什么我以这种方式设计了解决方案,但是这两种方法都可以解决我的问题。问候。 – Sugar

回答

13

的问题是,Form<Field>实现IForm<Field>但不IForm<IField>。除非它被标记为与out标识符协变,否则不能使用继承类(或接口)作为通用参数。但是,将界面标记为协变会严重限制使用(主要是在“仅输出”界面中制作,例如IEnumerable),因此它可能不适用于您。

一种方式来得到它的工作就是让TestMethod通用以及:

public static void TestMethod<T>(IForm<T> form) where T:IField 
{ 
    int i = 1; 
    i = i * 5; 
} 
10

您可以使用协方差,像这样:

interface IForm<out T> where T : IField 
{ 

} 

更多关于协变和逆变here

+1

协方差,当您允许派生类用作使用“out”关键字的泛型类型参数。在我发布的链接中,您可以看到以下语句: “因为IEnumerable接口的类型参数是协变的,所以可以将IEnumerable (IEnumerable(Of Derived)在Visual Basic中)的实例分配给IEnumerable类型的变量 “ –

7

其他人指出了错误消息背后的原因,但让我们暂时检查示例代码的设计。也许你在没有需要的情况下使用泛型。

你已经说过你正在使用在IField接口中声明的方法,所以可能不需要使你的IForm类是通用的 - 只需要存储对IField的引用,而不是通用参数'T'(无论如何它已经保证是一个IField)。

例如,使用方法:

public interface IForm 
{ 
    IEnumerable<IField> Fields { get; set; } 
} 

,而不是

public interface IForm<T> where T : IField 
{ 
    IEnumerable<T> Fields { get; set; } 
} 
+0

同意。另一个迹象是消费者代码将这个实例化为IForm 和表格的形式,表明IField是足够的。真正的测试无论是否具体类型T都需要成为面向外部API的一部分。 – Tormod