我以为我理解了通用约束,直到我遇到了这个问题。
public class DBase<T> : DbContext, IDisposable where T : DBase<T>T怎么可能是DBase<T>
如果可以的话,这意味着什么?
此代码编译并运行良好。我不是在解决问题。我只是不明白。
这里用的是
public class ChildDb : DBase<ChildDb>这也不能为我计算。它以类型参数的形式传递自己?
发布于 2018-12-31 05:49:10
T怎么可能是DBase<T>?
没有任何限制可以阻止泛型参数从自身派生出来。虽然你所举的例子并不能直接理解。顶点/顶点(https://en.wikipedia.org/wiki/Vertex_(geometry%29)?)呢?
摘自维基百科:

在几何学中,顶点(复数:顶点或顶点)是两个或多个曲线、线或边相交的点。作为这一定义的结果,两条线相交形成角的点和多边形和多面体的角是顶点。1
如何描述顶点(点)?
// very simplified example
public class Vertex
{
public int X { get; set; }
public int Y { get; set; }
}现在,我们如何在这个类中添加一个关系版本集合,但只允许从这个类派生的东西呢?
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>,允许顶点成为派生类的正确方法是使用这种类型的自引用泛型。
对不起,埃里克,我在细节上说错了。我从递归中得到了什么?
使用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;当然,你可以投它,但是你把它投到任何地方(那不是干的).如果它不是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();每次导航到顶点时,我们都会得到正确的派生类型,而不是基类。
发布于 2018-12-31 06:52:54
吴宇森在评论中发表了一个很棒的博客,其中的TLDR是:
此代码模式允许您声明必须扩展的超类(如果您正在编写其他人将使用的库,则可能不是由您进行扩展)才能使用,但是可以有许多方法/签名(由您编写),它们在编写时返回T,但实际上将返回子类型的对象(不是由您/您无法知道),因此它们可以以链式方式使用(就像大多数StringBuilder方法返回StringBuilder本身的方式一样),这样用户就可以不需要从父类型(由您编写的代码)转换为子类型(不是由您编写的),就可以调用.Append().AppendLine()。
有一个警告:它不是特别有用,因为只有继承树中最深的子节点才能被实例化。避免使用它
发布于 2018-12-31 07:28:20
作为一个有用的示例,它允许您在基类中有一些返回派生类型的方法或属性。
例如,在具有可链式方法的fluent构建器中,假设我们有一个基本构建器,它设置了一些公共属性。这些方法的输出类型应该是什么?
请参见以下示例:
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的约束,那么如何从Id方法返回TBuilder呢?
如果您说“为什么不返回ControlBuilder<TBuilder, TControl>”,因为如果您返回它,在方法链中调用.Id(“某某物”)之后,它将不会显示派生类方法,它只会显示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;
}
}现在我们可以像预期的那样使用它:
var txt = TextBoxBuilder.With().Id("textBox1").Text("Hello!").Build();https://stackoverflow.com/questions/53983914
复制相似问题