【问题标题】:Best way to refer to my own type引用我自己的类型的最佳方式
【发布时间】:2011-06-10 17:12:15
【问题描述】:
abstract class A<T> where T:A<T>
{
    public event Action<T> Event1;
}

class B : A<B>
{
    //has a field called Action<B> Event1;
}

有没有更优雅的方法来做到这一点?我希望基类中的东西(事件等)能够使用子类的类型。

【问题讨论】:

  • 小心,一个基类不应该直接知道它的子类,它会变成一个讨厌的技术债务。在这种情况下,因为它是一个通用的,它并不算太糟糕,但只是一般。
  • 你不是说where T:A&lt;T&gt;吗? Eric Lippert 已经写过这个:blogs.msdn.com/b/ericlippert/archive/2011/02/03/…

标签: c# generics


【解决方案1】:

您使用的模式实际上并没有实现您想要的约束。假设你想模拟“动物只能对自己同类的东西友好”:

abstract class Animal<T> where T : Animal<T>
{
    public abstract void GetFriendly(T t);
}

class Cat : Animal<Cat>
{
    public override void GetFriendly(Cat cat) {}
}

我们是否成功实现了期望的约束?没有。

class EvilDog : Animal<Cat>
{
    public override void GetFriendly(Cat cat) {}
}

现在邪恶的狗可以与任何猫友好,而与其他邪恶的狗不友好。

您想要的类型约束在 C# 类型系统中是不可能的。如果您需要由类型系统强制执行的这种约束,请尝试 Haskell。

有关详细信息,请参阅我关于此主题的文章:

http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx

【讨论】:

  • 我认为您指出了一个重要的限制,但根据实际要求,这可能是一个严重的问题,也可能不是一个严重的问题。例如,如果仅在自己的代码库中使用它,那么如果该模式被正确实现,则在风格上可能很明显。
  • 对此进行有趣的尝试。我完全不同意。您认为使用此技术存在的问题有一个简单的解决方案。 blog.theobjectguy.com/2011/06/…
【解决方案2】:

你所拥有的效果很好。事实上,它与您希望接口实现者使用您的类型的其他 .NET 接口和类型非常相似,例如:

public class MyClass : IEqualityComparer<MyClass>
{
    // From the interface IEqualityComparer
    public bool Equals(MyClass other) { ... }

    ...
}

【讨论】:

    【解决方案3】:

    我认为您不需要指定 T:A 的位置。

    当您使用 B 类时,T 将是 B:A

    这也被称为CRTP or Curiously recurring template pattern,是一个众所周知的成语。

    【讨论】:

      【解决方案4】:

      由于A是抽象的,你可以给A添加抽象方法,并从A和B调用它们,这将被强制实现该方法,将是调用者:

      abstract class A<T> where T:A
      {
          public event Action<T> Event1;
          public abstract void Method();
      
          public A(){Method();}
      }
      
      class B : A<B>
      {
          //has a field called Action<B> Event1;
          public void Method(){ //stuff }
      }
      

      在B实例化时,基类构造函数将调用仅在B中实现的Method(),强制调用B的实例。

      这允许 A 调用特定于子类的方法,而无需 A 具有特定的 Children 知识。缺点是所有孩子都必须实现 Method 或将其重新抽象给他们自己的孩子。

      【讨论】:

        【解决方案5】:

        我最近的question 被标记为与此重复。我完全同意这件事。所以我来这里看看答案并阅读 Eric 的帖子(确实很有趣)。您不能在编译时使用类型系统强制执行此操作,但您可以在运行时执行此操作。我的实现方式是:

        abstract class FooBase<T>
        {
            protected FooBase()
            {
                if (typeof(T) != GetType())
                {
                    throw new InvalidOperationException();
                }
            }
        }
        

        通过这样做,我们可以种下一只邪恶的狗的种子,但这只狗会在运行时被流产。

        【讨论】:

          猜你喜欢
          • 2010-09-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-04-23
          • 2021-12-14
          • 2016-02-01
          • 2011-03-28
          相关资源
          最近更新 更多