Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 递归约束:DBase<;T>;:其中T:DBase<;T>;什么意思?_C#_Generics_Constraints - Fatal编程技术网

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
的方法,您只会看到base
ControlBuilder
的方法。我建议您在实际操作中尝试一下,以获得更好的效果。希望有帮助:)
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();