我以为我理解通用的约束,直到遇到这个约束。
public class DBase<T> : DbContext, IDisposable where T : DBase<T>
如何成为DBase<T>
?
如果可以的话,这是什么意思?
这段代码可以编译并正常运行。我没有解决问题。我只是不明白。
在这里使用
public class ChildDb : DBase<ChildDb>
再一次,这不适合我。它本身作为类型参数传递吗?
答案 0 :(得分:3)
T
如何成为DBase<T>
?
没有限制可以防止从其自身派生通用参数。虽然您所提供的示例无法直接理解。 Vertex / Vertice呢?
摘录自维基百科:
在几何中,顶点(复数:顶点或顶点)是两个或更多曲线,直线或边相交的点。根据此定义,两条线相交形成角度的点以及多边形和多面体的角均为顶点。1
如何描述一个顶点(一个点)?
// very simplified example
public class Vertex
{
public int X { get; set; }
public int Y { get; set; }
}
现在我们如何向该类添加一组相关的Verticies,但仅允许从该类派生的内容?
public class Vertex<TVertex> : Vertex
where TVertex : Vertex<TVertex>
{
public IEnumerable<TVertex> Vertices { get; set; }
}
它是通用的说法:
public Vertex2
{
public IENumerable<Vertex2> Vertices { get; set; }
}
但是,当我从Vertex2派生时,我的Vertices
将始终必须为IEnumerable<Vertex2>
,并且允许Vertices成为派生类的正确方法是使用这种类型的自引用泛型。
对不起Erik,我在细节上没讲清楚。递归给我带来了什么?
使用Vertex2
,我们的派生类型将失去对其他派生属性的访问权限:
public class MyVertex2: Vertex2
{
public int Id { get; set; }
}
如此
var a = new MyVertex2 {Id = 1 };
var b = new MyVertex2 { Id = 2 };
a.Vertices = new List<Vertex2> { b };
b.Vertices = new List<Vertex2> { a };
// can't access Id because it's a Vertex2 not a MyVertex2
var bId = a.Vertices.First().Id;
确定可以投射它,然后将其投射到任何地方(不是DRY)……如果它不是MyVertex(MullReferencesException或InvalidCastException),该怎么办。
public class MyVertex: Vertex<MyVertex>
{
public int Id { get; set; }
}
var a = new MyVertex {Id = 1 };
var b = new MyVertex { Id = 2 };
a.Vertices = new List<MyVertex > { b };
b.Vertices = new List<MyVertex > { a };
var bId = a.Vertices.First().Id;
// or even
var aId = a.Vertices.First().Vertices.First();
每次导航到一个顶点时,我们都会得到正确的派生类型,而不是基类。
答案 1 :(得分:1)
John Wu在评论中发布了一个很棒的博客,其TLDR为:
此代码模式允许您声明必须扩展的超类(如果正在编写供其他人使用的库,则可能不是由您扩展),以便可以使用有一堆(由您编写)的方法/签名,当您编写它们时会返回T,但实际上将返回子类型为(您不是由您编写/您不知道)的对象< / em>,因此可以链式使用它们(就像大多数StringBuilder方法返回StringBuilder本身的方式一样,以便用户可以调用.Append()。AppendLine()),而无需强制转换为(在代码中(由您写)从父类型(由您写)到子类型(不是由您写)
有一个警告:它不是特别有用,因为只能实例化继承树中最深的子级。避免使用它
答案 2 :(得分:1)
作为一个有用的示例,它允许您在基类中具有一些返回派生类型的方法或属性。
例如,在具有可链接方法的流利的构建器中,假设我们有一个设置一些公共属性的基本构建器。这些方法的输出类型应该是什么?
请参见以下示例:
public abstract class Control
{
public string Id { get; set; }
}
public abstract class ControlBuilder<TBuilder, TControl>
where TBuilder : ControlBuilder<TBuilder, TControl>, new()
where TControl : Control, new()
{
protected TControl control;
protected ControlBuilder()
{
control = new TControl();
}
public static TBuilder With()
{
return new TBuilder();
}
public TControl Build()
{
control;
}
public TBuilder Id(string id)
{
control.Id = id;
return (TBuilder)this;
}
}
在没有ControlBuilder<TBuilder, TControl>
作为TBuilder
约束的情况下,如何从TBuilder
方法返回Id
?
如果说问为什么不返回ControlBuilder<TBuilder, TControl>
,因为如果返回它,则在方法链中调用.Id(“ something”)之后,它将不会显示派生类方法,而只会显示{ {1}}。
比方说,我们创建了一个ControlBuilder<TBuilder, TControl>
用于构建TextBoxBuilder
:
TextBox
现在我们可以按预期使用它了:
public class TextBox : Control
{
public string Text { get; set; }
}
public class TextBoxBuilder : ControlBuilder<TextBoxBuilder, TextBox>
{
public TextBoxBuilder Text(string text)
{
control.Text = text;
return this;
}
}