c#泛型自引用声明

本文关键字:声明 自引用 泛型 | 更新日期: 2023-09-27 18:19:49

我一直在读Albaharis的"Nutshell中的C#5.0",我在Generics部分遇到了这个问题,据说它是合法的:

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

这对我来说毫无意义,尽管我已经仔细阅读了整章。我一点也听不懂。

有人能用一些可以理解的名字来解释吗,比如:

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

在现实世界的应用场景中,这种用法是合适和有用的?

c#泛型自引用声明

这意味着T必须继承自Person<T>

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

例如:

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

当您使用某些外部库或框架(您不能或不想修改)时,它很有帮助。例如,您有这个库中的类User,将使用它的开发人员肯定会定义从它继承的CustomUser类(只是为了添加一些自定义字段)。还让我们想象一下,User类对另一个用户有一些引用,例如:creator和deletor(它们显然是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;

我可能会迟到一点,但我想分享一个不真实的世界应用程序场景来取乐:)

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

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

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

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

想象一个框架,其中crud对象基于crudBase,并且所有内容都继承自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()函数。