【问题标题】:c# generic self-referencing declarationsc# 泛型自引用声明
【发布时间】:2020-05-30 21:38:39
【问题描述】:

我一直在阅读 Albaharis 的“C# 5.0 in A Nutshell”,我在泛型部分遇到过这个,据说它是合法的:

class Bar<T> where T : Bar<T> { ... }

虽然我已经仔细阅读了整章,但这对我来说毫无意义。一点都看不懂。

有人可以用一些可以理解的命名来解释它,例如:

class Person<T> where T : Person<T> { ... }

以及这种用法合适且有用的真实应用场景?

【问题讨论】:

  • 可能有点帮助,尽管它不是问题的重复或解决方案:stackoverflow.com/questions/6618134/… 问题中有一个关于动物和鸭子的例子
  • 如果我没记错的话,这叫做“奇怪的重复模板模式”

标签: c# self-reference


【解决方案1】:

表示T必须继承自Person&lt;T&gt;

这是在基类中创建特定于类型的方法或属性或参数的典型方法,特定于实际的后代。

例如:

public abstract class Base<T> where T : Base<T>, new()
{
    public static T Create()
    {
        var instance = new T();
        instance.Configure(42);
        return instance;
    }

    protected abstract void Configure(int value);
}

public class Actual : Base<Actual>
{
    protected override void Configure(int value) { ... }
}

...

 Actual a = Actual.Create(); // Create is defined in Base, but returns Actual

【讨论】:

    【解决方案2】:

    当您使用一些外部库或框架(您不能或不想修改)时,这很有帮助。例如,您有来自该库的类User,并且肯定会使用它的开发人员将定义从它继承的CustomUser 类(仅用于添加一些自定义字段)。还让我们想象一下,User 类有一些对其他用户的引用,例如:创建者和删除者(显然是 CustomUser 类型的实例)。在这种情况下,通用自引用声明可以提供很好的帮助。我们将后代类型(CustomUser) 作为参数传递给基类(User),所以在User 类声明中,我们可以设置创建者和删除者的类型精确,就像它们在未来(CustomUser),所以不需要强制转换

    public class User<TCustomUser> where TCustomUser : User<TCustomUser>
    {
        public TCustomUser creator {get;set;}
        public TCustomUser deletor {get;set;}
    
        //not convenient variant, without generic approach
        //public User creator {get;set;}
        //public User deletor {get;set;}     
    }
    
    public class CustomUser : User<CustomUser>
    {
        //custom fields:
        public string City {get;set;}
        public int Age {get;set;}
    }
    

    用法:

    CustomUser customUser = getUserFromSomeWhere();
    //we can do this
    var creatorsAge = customUser.creator.Age;
    //without generic approach:
    //var creatorsAge = ((CustomUser)customUser.creator).Age;
    

    【讨论】:

      【解决方案3】:

      我参加聚会可能有点晚了,但我想分享一个不真实的应用场景来取乐:)

      // self referencing list in c#
      // we cant use generic type syntax: new List<List<List..
      // but dynamic keyword comes to save us
      
      var list = new List<dynamic>();
      list.Add(list); // the "FBI! open up" part
      Console.WriteLine(list[0][0][0][0][0][0][0].Count); // 1
      

      【讨论】:

        【解决方案4】:

        当您要编写一系列类并且您意识到 80%(选择一个数字)的代码基本上是相同的,只是它因 TYPE 不同而异时,这也很有帮助。

        编写泛型允许您在基类中捕获所有重复代码并重用它。

        上面的特定模式很重要/必要,因为您希望 T 成为您要编写的类。

        想象一个框架,其中 crud 对象基于 crudBase 并且一切都继承自该框架。 进一步想象一下,您有一个基类可以帮助您查询这些对象 (queryBase),并且 crudBase 和 queryBase 类将是 1:1。

        将 queryBase 设为泛型很简单,因为它的声明方式相当明显

        public abstract class queryBase<T> where T : crudBase
        {
         public list<T> FindMatches(string SearchCriteria){}
        
          }
        

        如果没有泛型,这必须在每个具体类中,因为返回类型会发生变化。泛型很棒。

        不太明显的是如何使用 crudBase 实现相同级别的 GENERIC 必杀技。 假设您的子类中已有 70% 的样板 CRUD 代码,但还有 10% 的逻辑需要引用类型。
        (选择一个数字,% 数字不重要)

        GENERIC 解决方案在这里不太明显。在第一种情况下,您的 GENERIC 类使用 T 引用不同的类。在这种情况下,您想用 T 引用同一个类。

        使用上述模式,您实际上可以实现这一点:

        public class crudBaseGeneric<T> where T : crudBaseGeneric<T>
        {
             public <T> CopyMe(){}
          }
        

        在这里,您将把您的基类重新定义为泛型,您将能够捕获最后的 10%。

        同样,如果没有泛型,我必须在每个具体类中复制粘贴我的 CopyMe() 函数。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-11-17
          • 1970-01-01
          • 2017-12-29
          • 1970-01-01
          • 1970-01-01
          • 2015-12-14
          相关资源
          最近更新 更多