C# 递归约束:DBase<;T>;:其中T:DBase<;T>;什么意思?
我以为我理解一般约束,直到我遇到这个C# 递归约束:DBase<;T>;:其中T:DBase<;T>;什么意思?,c#,generics,constraints,C#,Generics,Constraints,我以为我理解一般约束,直到我遇到这个 public class DBase<T> : DbContext, IDisposable where T : DBase<T> 公共类DBase:DbContext,IDisposable,其中T:DBase 如何才能成为DBase? 如果可以,这意味着什么 这段代码编译并运行良好。我没有解决问题。我就是不明白 这里用的是 public class ChildDb : DBase<ChildDb> 公共类
public class DBase<T> : DbContext, IDisposable where T : DBase<T>
公共类DBase:DbContext,IDisposable,其中T:DBase
如何才能成为DBase
?如果可以,这意味着什么 这段代码编译并运行良好。我没有解决问题。我就是不明白 这里用的是
public class ChildDb : DBase<ChildDb>
公共类ChildDb:DBase
这对我来说也算不上。它将自身作为类型参数传递
T
如何成为DBase
不存在阻止泛型参数从自身派生的限制。虽然你给出的例子不能直接理解。一辆汽车怎么样
摘自维基百科:
在几何中,顶点(复数:顶点或顶点)是两条或多条曲线、直线或边相交的点。根据此定义,两条线相交形成角度的点,多边形和多面体的角是顶点
如何描述顶点(点)
现在,我们如何向这个类添加一个关系垂直的集合,但只允许从这个类派生的东西
public class Vertex<TVertex> : Vertex
where TVertex : Vertex<TVertex>
{
public IEnumerable<TVertex> Vertices { get; set; }
}
所以
var a=newmyvertex2{Id=1};
var b=新的MyVertex2{Id=2};
a、 顶点=新列表{b};
b、 顶点=新列表{a};
//无法访问Id,因为它是Vertex2而不是MyVertex2
var bId=a.Vertices.First().Id;
当然你可以投下它,但是你却到处投下它(那不是)。。。如果它不是MyVertex(MullReferencesException或InvalidCastException),该怎么办
公共类MyVertex:Vertex
{
公共int Id{get;set;}
}
var a=新的MyVertex{Id=1};
var b=新的MyVertex{Id=2};
a、 顶点=新列表{b};
b、 顶点=新列表{a};
var bId=a.Vertices.First().Id;
//甚至
var aId=a.Vertices.First().Vertices.First();
每次导航到顶点时,我们都会得到正确的派生类型,而不是基类。John Wu在评论中发表了一篇很棒的博客,其TLDR是: 这个代码模式允许您声明一个必须扩展的超类(如果您正在编写一个其他人将使用的库,则可能不是由您扩展),但可以有一组方法/签名(由您编写),这些方法/签名在编写时返回T,但实际上将返回子类型的对象(不是由您编写的/您不知道),因此它们可以以链式方式使用(就像大多数StringBuilder方法返回StringBuilder本身的方式,以便用户可以调用.Append().AppendLine()),而无需(在非由您编写的代码中)从父类型(由您编写)强制转换为子类型(非由您编写)
有一个警告:它不是特别有用,因为只能实例化继承树中最深的子级。请避免将它用作有用的示例,它允许您在基类中具有一些返回派生类型的方法或属性 例如,在具有可链接方法的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;
}
}
现在我们可以按预期使用它:
var txt = TextBoxBuilder.With().Id("textBox1").Text("Hello!").Build();
通过这种方式,您可以将所有泛型方法设置为要求T与声明类型属于同一类型。请参阅,这被称为or CRTP.INTERNATIONAL。是否要将其中任何一个作为答案发布,以便我确认?这通常对于创建具有可链接方法的fluent Builder非常有用。在这里,您可以看到,此模式对于让e基类,有一个返回派生类实例的方法。这三个方法中的任何一个似乎都是有效的答案。我将等待一周,看看哪一个是上乘的。很好的示例,但需要数学知识:)很抱歉,Erik,我失去了细节中的重点。通过递归我得到了什么?这不是更好吗:“where t:Vertex”其中Vertex是原始类而不是泛型。很明显,我仍然缺少一些东西。@b如果不是泛型类,则无法导航到任何其他顶点,因为您没有
顶点
类。我明白了。谢谢你的澄清。总之,所有这些都是关于类能够使用正确的子体类型。我只是在看代码,而不是它是如何调用的。我知道这个示例似乎有点复杂,但是如果您尝试在没有ControlBuilder作为TBuilder约束的情况下进行操作并尝试回答,那么如何从Id方法返回TBuilder,您将看到您是否没有该约束,然后在调用TextBoxBuilder.With().Id之后(“textBox1”)
不会出现TextBoxBuilder
的方法,您只会看到baseControlBuilder
的方法。我建议您在实际操作中尝试一下,以获得更好的效果。希望有帮助:)
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;
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();
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;
}
}
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();