强制执行泛型接口子类型

本文关键字:类型 泛型接口 强制执行 | 更新日期: 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();
    }
}