C# 泛型自引用声明

c# generic self-referencing declarations

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

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

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

谁能用一些易于理解的命名来解释它,比如:

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

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

表示T必须继承自Person<T>

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

例如:

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

当您使用某些外部库或框架(您不能或不想修改)时,它很有帮助。例如,你有来自这个库的 class User 并且肯定会使用它的开发人员将定义从它继承的 CustomUser class(只是为了添加一些自定义字段)。另外让我们想象一下,User class 有一些对其他用户的引用,例如:创建者和删除者(显然是 CustomUser 类型的实例)。在这种情况下,通用 自引用声明 可以提供很好的帮助。我们会将后代类型 (CustomUser) 作为参数传递给基础 (User) class,因此在 User class 声明中我们可以设置创建者类型和删除器完全,因为它们将在未来(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;

我可能来晚了一点,但我想分享一个虚幻世界的应用场景来娱乐一下:)

// 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

当您要编写一系列 class 代码并且您意识到 80%(选择一个数字)的代码除了因类型不同外基本相同时,这也很有帮助。

编写泛型可以让您捕获基础中的所有重复代码 class 并重新使用它。

上面的特定模式是 important/necessary 因为您希望 T 成为您尝试编写的 class。

想象一个框架,其中 crud 对象基于 crudBase 并且所有内容都继承自它。 进一步想象一下,您有一个基础 class 可以帮助您查询这些对象 (queryBase),并且会有一个 1:1 with crudBase 和 queryBase classes.

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

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

  }

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

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

GENERIC 解决方案在这里不太明显。在第一种情况下,您 GENERIC class 引用了一个不同的 class 和 T。在这种情况下,您希望使用 T.

引用相同的 class

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

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

在这里,您将把您的基础 class 重新定义为通用的,您将能够捕获最后的 10%。

同样,如果没有泛型,我必须在每个具体 class.

中复制粘贴我的 CopyMe() 函数