强制执行泛型接口子类型
本文关键字:类型 泛型接口 强制执行 | 更新日期: 2023-09-27 18:00:05
我有一个通用接口(MyInterface<T>
),它由下面示例中的类ChildA
实现:
public interface MyInterface<T>
{
MyObj<T> GetObj(); // Irrelevant
}
class ChildA : MyInterface<ChildA>
{
// Irrelevant:
MyObj<ChildA> GetObj() {
return new MyObj<ChildA>();
}
}
这是可行的,但我需要确保<T>
总是具有实现类的的类型,因此在这种情况下,T
应该总是ChildA
的类型,因为它是由ChildA
实现的。
另一个正确的实现可能是这样的,例如:
class ChildB : MyInterface<ChildB> { ... }
但目前,这种不正确的实现也是可能的,而不应该是:
class ChildA : MyInterface<ChildB> { ... }
有办法强制执行吗?
不能强制将泛型类型参数约束为实现类型。
可用的类型约束如下:
where T : struct
where T : class
where T : new()
where T : <base class name>
where T : <interface name>
where T : U
C#中没有什么比where T : self
更好的了。事实上,这甚至没有意义,因为这样的事情无法有意义地执行。此外,它根本不符合协方差/逆变换的概念,而且通常从中继承会很奇怪。
你能做的最接近的事情是:
public interface IMyInterface<T> where T : IMyInterface<T>
{
MyObj<T> GetObj();
}
为什么说不通
假设你可以这样做:
public interface IMyInterface<T> where T : self // this syntax does not exist in C#
{
MyObj<T> GetObj();
}
现在,所有实现类型都必须将自己用作类型参数。但你仍然可以这样做:
public class ChildC<T> : IMyInterface<T> where T : self
{
/* ... */
}
这将绕过你的限制。
有办法强制执行吗?
嗯,没有通用约束。你可以反思一下,尽管我会投反对票:
public abstract class BaseChild<T> : MyInterface<T>
{
protected BaseChild()
{
if (typeof(T) != this.GetType())
{
throw new InvalidOperationException(string.Format(
"Type {0} is not supported as valid type parameter for type {1}",
typeof(T).Name, this.GetType().Name));
}
}
}
示例:
class ChildA : BaseChild<int> { }
// Bang! throws
var instance = new ChildA();
class ChildB : BaseChild<ChildB> { }
// Ok here
var instance = new ChildB();
您不能这样做,但您可以创建自己的控件,比较接口的泛型类型和类的类型。参见示例:
class ChildA : MyInterface<ChildB>
{
public ChildA()
{
this.ValidateGenericType();
}
public MyObj<ChildB> GetObj()
{
return new MyObj<ChildB>();
}
protected void ValidateGenericType()
{
//throws an Exception because ChildB is different of ChilA
if (this.GetType().Name != this.GetType().GetInterfaces()[0].GetGenericArguments()[0].Name)
{
throw new Exception("The generic type must be of type ChildA.");
}
}
}
似乎应该使用扩展方法,而不是为此目的强制使用一些接口
public interface ISomeInterface {}
public class Child: ISomeInterface {}
public class OtherChild : ISomeInterface { }
public static class MyInterfaceExtensions
{
public static MyObj<T> GetMyObj<T>(this T child) where T : ISomeInterface
{
return new MyObj<T>();
}
}
public static class Test
{
public static void RunTest()
{
var child = new Child();
var otherChild = new OtherChild();
MyObj<Child> myObj = child.GetMyObj();
MyObj<OtherChild> myOtherObj = otherChild.GetMyObj();
}
}