执行通用接口子类型

时间:2016-08-03 12:06:27

标签: c# .net generics inheritance interface

我有一个通用接口(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应始终为{{1}类型1}},因为它是由ChildA实现的。

另一个 正确的 实施可能是这样的,例如:

ChildA

但目前,这种 不正确的 实施也是可能的,但它不应该是:

class ChildB : MyInterface<ChildB> { ... }

有没有办法强制执行此操作?

4 个答案:

答案 0 :(得分:7)

您不能强制将泛型类型参数约束为实现类型。

可用的type constraints如下:

  • 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
{
    /* ... */
}

这将绕过你的限制。

答案 1 :(得分:2)

  

有没有办法强制执行此操作?

好吧,不是通用约束。你可以用反思做到这一点虽然我投反对票:

<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>

<table>

    <tr class="table-row">
        <td>
            <a href="#" class="item-name">Item 1</a>
            <input type="text" value="[item]" class="input-item" id="item-1">
        </td>
        <td>
            <a href="#" class="item-name">Price 1</a>
            <input type="text" value="[item]" class="input-item" id="price-1">
        </td>
        <td><a href="#" class="edit-button">Edit</a></td>
    </tr>

    <tr class="table-row">
        <td>
            <a href="#" class="item-name">Item 2</a>
            <input type="text" value="[item]" class="input-item" id="item-2">
        </td>
        <td>
            <a href="#" class="item-name">Price 2</a>
            <input type="text" value="[item]" class="input-item" id="price-2">
        </td>
        <td><a href="#" class="edit-button">Edit</a></td>
    </tr>
</table>

示例:

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(); 

答案 2 :(得分:1)

您无法执行此操作,但您可以创建自己的控件,比较界面的泛型类型和类的类型。参见示例:

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.");
        }
    }
}

答案 3 :(得分:1)

您似乎应该使用扩展方法而不是为此目的强制执行某些接口

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();
    }
}