【问题标题】:Enforcing generic interface childs type强制通用接口子类型
【发布时间】:2016-12-09 03:38:27
【问题描述】:

我有一个通用接口 (MyInterface<T>),它由下面示例中的 ChildA 类实现:

public interface MyInterface<T>
{
    MyObj<T> GetObj(); // Irrelevant
}

class ChildA : MyInterface<ChildA>
{
    // Irrelevant:
    MyObj<ChildA> GetObj() {
        return new MyObj<ChildA>();
    }
}

这可行,但我需要确保 &lt;T&gt; 始终具有 implementing 类的类型,因此在这种情况下 T 应该始终是 ChildA 类型,因为它由ChildA实现。

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

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

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

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

有没有办法强制执行?

【问题讨论】:

  • 我不确定您要在这里做什么。可以强制执行自身的类型,但几乎没有收获。其背后的动机是什么?
  • 你为什么要这个?
  • @MioBambino 我正在使用它来实现数据传输对象。这一切都已经工作了,但正如我提到的,仍然可以将其设置为另一种类型。这就是为什么我需要强制这种情况不会发生。
  • 这种方法怎么样? public interface MyInterface { MyObj&lt;MyInterface&gt; GetObj(); }
  • 这被称为Curiously Recurring Template Pattern,顺便说一句,不,没有办法在C#中添加这个限制。引用@EricLippert,"[模板] 似乎是对机制的滥用,而不是对程序“业务领域”的概念建模

标签: c# .net generics inheritance interface


【解决方案1】:

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

可用的type constraints 如下:

  • where T : struct
  • where T : class
  • where T : new()
  • where T : &lt;base class name&gt;
  • where T : &lt;interface name&gt;
  • 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
{
    /* ... */
}

这会绕过你的限制。

【讨论】:

  • 最后的声明不是也需要where T : self 才能编译吗?
  • @Groo 可能 :) 取决于语言作者希望它如何工作。
  • 现在您还需要将限制添加到泛型类中,因此假设"self" 约束以相同的方式应用,它很可能与该类一起正常工作。如果我是对的,它也可能需要创建类sealed,因为你不能“合法地”从它派生。
  • @Groo OK,添加了约束。
  • 好吧,如果假设的self 表示约束的声明者或他的孩子,那对我来说仍然有意义。因为只有声明者,肯定没有意义。我说的对吗?
【解决方案2】:

有没有办法强制执行?

嗯,没有通用约束。尽管我会投反对票,但您可以通过反思来做到这一点:

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

【讨论】:

    【解决方案3】:

    您不能这样做,但您可以创建自己的控件来比较接口的泛型类型和类的类型。看例子:

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-09
        • 1970-01-01
        • 1970-01-01
        • 2016-08-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多