我可以在通用类中使用不相关的类型作为约束吗?

时间:2014-02-06 15:31:02

标签: c# .net generics

我想创建一个泛型类,其中T参数可以是其他类型集合中的任何实例。 所以这段代码编译得很好:

public interface TestA { }
public interface TestB { }
public class Test<T> where T : TestA, TestB
{

}

然而,当我尝试像这样使用它时

var a = new Test<TestA>();
var b = new Test<TestB>();

我遇到两个编译错误:

  

错误1类型'TestA'不能用作类型参数'T'   泛型类型或方法'测试'。没有隐含的参考   从'TestA'转换为'TestB'。

     

错误2类型'TestB'不能用作类型参数'T'   泛型类型或方法'测试'。没有隐含的参考   从'TestB'转换为'TestA'。

我很困惑为什么这不起作用。我该如何解决这个问题?理想情况下,我希望TestA和TestB类没有关系(例如,它们之间没有继承)。

3 个答案:

答案 0 :(得分:3)

类型必须遵循在泛型类型上定义的所有约束(AND,而不是OR)。

如果要同时允许TestS或Test,则应定义基本接口:

public interface TestBase { }
public interface TestA : TestBase { }
public interface TestB : TestBase { }
public class Test<T> where T : TestBase
{

}

答案 1 :(得分:1)

您指定该类必须实现这两个接口。所以你必须给它一个实现两个接口的类,而不仅仅是A或B

public class LikeThis : TestA, TestB
{

  // have both A and B's properties, methods, etc.
}

var a = new Test<LikeThis>();

另一个选择是使TestA和TestB继承自基础接口

public interface IBaseInterface  { }
public interface IInterfaceA { }
public interface IInterfaceB { }

public class Test<T> where T : IBaseInterface
{

}

var a = new Test<IBaseInterface>();

但很难说出你要在这里完成什么。 Test类可能会将对IBaseInterface的引用作为属性,但它不知道它是InterfaceA还是InterfaceB。

也许如果你告诉我们你想要实现的目标,我们可以建议一个更好的解决方案。

答案 2 :(得分:1)

对type参数的约束是连接(AND),因此在那里使用的任何类型都必须实现这两个接口。

正如已经提到的,一种方法是为两个接口创建一个超级接口,并将类型参数约束为(注意我添加了通常的接口前缀I):

public interface IBase { }
public interface ITestA : TestBase { }
public interface ITestB : TestBase { }
public class Test<T> where T : IBase {
}

这样做的缺点是ITestAITestB的实施者不仅可以用作Test<T>中的类型参数,还可以用作实现IBase的任何其他类型。

另一个选择是提供一个不能从程序集外部继承的超类,并从中创建两个子类,每个所需的接口类型一个:

public interface ITestA { }
public interface ITestB { }
public abstract class Test<T> {
    internal Test() { }
}
public class Test1<T> : Test<T> where T : ITestA { }
public class Test2<T> : Test<T> where T : ITestB { }

现在Test<T>超类不能从程序集外部继承(它有一个internal构造函数)。您的类(Test1<T>Test2<T>)只使用超类的逻辑,并且每个类都使用一个所需的接口作为约束。客户端代码必须根据他们想要使用的接口约束来选择使用哪一个。

此外,如果您希望在Test<T>内部使用接口中的公共代码,则应将其提取到超级接口并使您的超类受其约束,从而导致两种方法的混合:

public interface IBase { }
public interface ITestA : IBase { }
public interface ITestB : IBase { }
public abstract class Test<T> where T : IBase {
    internal Test() { }
}
public class Test1<T> : Test<T> where T : ITestA { }
public class Test2<T> : Test<T> where T : ITestB { }