【问题标题】:Recursive generic types递归泛型类型
【发布时间】:2009-03-15 09:18:36
【问题描述】:

是否可以在 C# 中定义引用自身的泛型类型?

例如我想定义一个 Dictionary 将其类型保存为 TValue(对于层次结构)。

Dictionary<string, Dictionary<string, Dictionary<string, [...]>>>

【问题讨论】:

  • 不,这是不可能的。您能否更具体地说明您要实现的目标?
  • 大声笑 Earwicker,你必须同意这很奇怪 ;) ...我也认为它没有(直接)..
  • 我认为人们会感到困惑,因为一个类不能从自身继承(很明显,或者只要它有任何字段,它就会具有无限大小),也不能从类型参数继承泛型,但是类自己的名称和类型参数可以出现在泛型基类的类型参数中就好了。

标签: c# generics


【解决方案1】:

试试:

class StringToDictionary : Dictionary<string, StringToDictionary> { }

然后你可以写:

var stuff = new StringToDictionary
        {
            { "Fruit", new StringToDictionary
                {
                    { "Apple", null },
                    { "Banana", null },
                    { "Lemon", new StringToDictionary { { "Sharp", null } } }
                }
            },
        };

递归的一般原则:想办法给递归模式一个名字,这样它就可以通过名字引用自己。

【讨论】:

  • 您的代码要求泛型类型是可子类化的 - 如果不是,我们可以使用 using 来定义别名,但不幸的是,您不能将 using 与开放的泛型类型一起使用 - 这也意味着我们可以' t 定义开放泛型类型的 Curried 或部分应用程序。这是 C# 的一个令人发狂的限制。
【解决方案2】:

另一个例子是通用树

public class Tree<TDerived> where TDerived : Tree<TDerived>
{
    public TDerived Parent { get; private set; }
    public List<TDerived> Children { get; private set; }
    public Tree(TDerived parent)
    {
        this.Parent = parent;
        this.Children = new List<TDerived>();
        if(parent!=null) { parent.Children.Add(this); }
    }
    public bool IsRoot { get { return Parent == null; } }
    public bool IsLeaf { get { return Children.Count==0; } }
}

现在使用它

public class CoordSys : Tree<CoordSys>
{
    CoordSys() : base(null) { }
    CoordSys(CoordSys parent) : base(parent) { }
    public double LocalPosition { get; set; }
    public double GlobalPosition { get { return IsRoot?LocalPosition:Parent.GlobalPosition+LocalPosition; } }
    public static CoordSys NewRootCoordinate() { return new CoordSys(); }
    public CoordSys NewChildCoordinate(double localPos)
    {
        return new CoordSys(this) { LocalPosition = localPos };
    }
}

static void Main() 
{
    // Make a coordinate tree:
    //
    //                  +--[C:50] 
    // [A:0]---[B:100]--+         
    //                  +--[D:80] 
    //

    var A=CoordSys.NewRootCoordinate();
    var B=A.NewChildCoordinate(100);
    var C=B.NewChildCoordinate(50);
    var D=B.NewChildCoordinate(80);

    Debug.WriteLine(C.GlobalPosition); // 100+50 = 150
    Debug.WriteLine(D.GlobalPosition); // 100+80 = 180
}

请注意,您不能直接实例化Tree&lt;TDerived&gt;。它必须是树中节点类的基类。想想class Node : Tree&lt;Node&gt; { }

【讨论】:

  • 我不太了解sn-p。如果您要使用 Tree&lt;TDerived&gt; 的派生类型,为什么还要费心 Tree&lt;TDerived&gt; where TDerived : Tree&lt;TDerived&gt; 中的 where 约束?在您的主代码中,您只使用派生类,那么Tree&lt;TDerived&gt; 声明中的where 约束是为了什么目的?
  • 为了确保继承的类型在实现TDerived Parent { get; } 等基本属性时返回正确的类型,无需在派生类中显式声明CoordSys Parent { get; } 即可自动满足。重要的是所有不存在的代码。
  • @JohnP - 在我的示例中,它说CoordSysTree&lt;CoordSys&gt;。类型是数据结构和数据本身,而不是 Node&lt;T&gt; 是数据结构,T 是数据。使用递归泛型类型可以简化很多代码,并且泛型约束赋予T 超越object 的权力,使其能够自我感知其方法和属性。
  • 我了解递归 CoordSys : Tree&lt;CoordSys&gt; 声明及其作用 - 这是确保某种类型的“自我”引用的一种非常简洁的方式,因此它对于包含的各种链接列表/结构的声明很有用指向它们自己类型的对象的引用/指针。而且我理解通用 Tree&lt;CoordSys&gt; 实际上声明了这些引用应该是什么。但是,如果我们在您的代码中删除了Tree&lt;TDerived&gt; 声明中的where TDerived : Tree&lt;TDerived&gt;,那么问题会是什么——如果没有它在您的代码中,一切都不会正常工作吗?
  • 我可以看到public class Tree&lt;TDerived&gt; where TDerived : Tree&lt;TDerived&gt; 中的where 可能有用,例如,如果我们要声明其他一些SpecialList&lt;Tderived&gt; : Tree&lt;TDerived&gt; 类-Tree&lt;TDerived&gt; 类中的where 将确保我们只能将SpecialList 用于继承自Tree&lt;TDerived&gt; 的类型TDerived。但在您的示例中,这不会在任何地方发生,也没有在任何地方实例化 Tree&lt;TDerived&gt;,所以我不明白 where 如何更改任何功能或确保任何行为。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-03
  • 2011-07-21
  • 2020-02-13
相关资源
最近更新 更多