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() 函数
我一直在阅读 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() 函数