使用层次结构和命名索引器的集合

时间:2016-01-13 15:06:36

标签: c#

我不确定我的问题的标题是否有意义。但基本上,我想在泛型集合中实现层次结构,但是我不想调用泛型方法或索引器,而是希望将子项命名为可访问的。很像,如果你愿意,一个完全合格的名字。

就结构而言,以下是我要创建的示例:

Root(1)
    SubContainer1(2)
        SubItem1(3)
    SubContainer2(4)
    SubContainer3(5)
        SubItem2(6)
        SubItem3(7)

以下是我想在代码中访问这些项目/对象/值的方法:

Root.SubContainer1.SubItem1

将产生/返回(int)3

现在,我完全知道我可以使用类来创建这种类型的结构:

namespace SomeNS
{
    public class Root
    {
        public const int DefaultValue = 1;
        public class SubContainer1
        {
            public const int DefaultValue = 2;
            public class SubItem1
            {
                public const int DefaultValue = 3;
            }
        }
    }
}

然后像这样访问它们:

SomeNS.Root.SubContainer1.SubItem1.DefaultValue

虽然此时需要动态生成此结构,但在最终状态/使用中,使用该库的开发人员不应该能够在结构中添加或删除项目。

但是,我想知道是否有另一种方法来制作这种类型的结构,而没有所有的类声明?如果还有一种方法也可以摆脱“DefaultValue”属性并且只是以某种方式产生int ...我应该添加项目列表可以是NDepth。

如果这不可能,我会坚持课堂宣言,但如果有更好的方法,我真的想学习如何做。

3 个答案:

答案 0 :(得分:0)

这可能是一个想法:

public class SubContainer
{
    public int DefaultValue { get; set; }
    public List<SubItem> SubItems { get; set;}
}

public class SubItem
{
    public int DefaultValue { get; set; }
}

public class Root
{
    public List<SubContainer> SubContainers { get; set; }
}

Root包含SubContainer的集合,SubContainer包含SubItem的集合。

通过这种方式,您可以构建层次结构。

完成此操作后,您可以执行以下操作:

SubItem first = root[0].SubItems[0].DefaultValue;

SubItem对象中维护的第一个SubContainer返回第一个Root

您提到删除了DefaultValue字段/属性,但我仅将其作为示例包含在内。

  

我想在泛型集合中实现层次结构,但我不想调用泛型方法或索引器,而是希望将子项命名为可访问的。很像,如果你愿意,一个完全合格的名字。

我不确定这是否可能!

答案 1 :(得分:0)

好吧,你可以尝试利用词典

Root["SubContainer1"]["SubItem1"].Value

然后你的代码可能是:

{{1}}

然后获取值:

{{1}}

这确实支持N深度,但我不知道它是否更好&#34;。如果名称更改或拼写错误,您可能会遇到错误

答案 2 :(得分:0)

如果您不需要非叶节点的值,则可以使用ExpandoObject

dynamic root = new ExpandoObject();
root.SubContainer1 = new ExpandoObject();
root.SubContainer1.SubItem1 = 3;
root.SubContainer2 = new ExpandoObject();
root.SubContainer3 = new ExpandoObject();
root.SubContainer3.SubItem2 = 6;
root.SubContainer3.SubItem3 = 7;

除了非叶子节点没有值之外,这会创建您在问题中提供的相同层次结构。

或者,通过为值保留一个属性名称并一致地使用它,您可以通过执行类似这样的操作在非叶节点上具有值(在您的类中包含using System.Dynamic):

dynamic root = new ExpandoObject();
root.Value = 1;
root.SubContainer1 = new ExpandoObject();
root.SubContainer1.Value = 2;
root.SubContainer1.SubItem1 = new ExpandoObject();
root.SubContainer1.SubItem1.Value = 3;
root.SubContainer2 = new ExpandoObject();
root.SubContainer2.Value = 4;
root.SubContainer3 = new ExpandoObject();
root.SubContainer3.Value = 5;
root.SubContainer3.SubItem2 = new ExpandoObject();
root.SubContainer3.SubItem2.Value = 6;
root.SubContainer3.SubItem3 = new ExpandoObject();
root.SubContainer3.SubItem3.Value = 7;

这会创建您在问题中提供的相同层次结构,但需要注意的是,所有int值都是通过最后添加的.Value来访问的。你可以使用混合解决方案,其中叶节点只是整数,但我认为这将导致更复杂的代码,并且更容易搞砸。

请注意这是所有代码。其他任何地方都没有SubContainer1的定义。 ExpandoObject是一个内置的.Net类。所以只需运行上面的代码,你就会看到它按原样运行。

如果您不熟悉dynamic,这意味着即使编译器无法知道dynamic对象上存在任何此类属性/字段/方法,您的代码也会编译。它的创建完全是为了处理像ExpandoObject或匿名对象这样的类,因此检查属性/字段/方法名称会延迟到运行时。

当您在根root.SubContainer = expression;上执行ExpandoObject时,此会在运行时创建属性并将其expression分配。当expression本身是new ExpandoObject时,您可以使用该子属性遵循相同的模式,并使用它们分配新属性。没有什么能阻止你拥有Root.SubContainer1.SubItem1.SubSubItem1.SubSubSubItem1.SubSubSubSubItem1

响应移动目标,更新

现在您已经声明要求生成的对象需要是不可变的,您是否考虑过匿名类型?

var root = new {
   Value = 1,
   SubContainer1 = new {
      Value = 2,
      SubItem1 = new {
         Value = 3
      }
   },
   SubContainer2 = {
      Value = 4
   },
   SubContainer3 {
      Value = 5,
      SubItem2 = new {
         Value = 6
      },
      SubItem3 = new {
         Value = 7
      }
   }
};

这是不可变的,在实例化后无法更改,但不能从var方法返回,因为除了var之外,编译器不能隐式强类型化关键字,不允许返回值(您必须显式声明返回值和方法/属性/字段的类型,并且不允许var,您必须使用object)。这意味着如果你没有在与它的使用相同的方法中声明它,你就不会得到intellisense或编译时检查(如果你使用了var root = ...,它将被编译器理解,但是,你不能从原来的方法中返回它。

在任何情况下,真的不应该使用匿名对象来简单地击败C#的整个严格类型方面。

这听起来几乎就像你需要使用不同的语言来实现你的目标。你要么:

  • 创建一个内联类型为var的匿名对象,并在同一方法中使用它(以获取设计时智能感知和编译时属性检查)。你会有不变性。
  • 创建一个匿名对象并将其作为类型object返回,并放弃设计时和编译时任何。你会有不变性。
  • 使用ExpandoObject并接受可变性,同时放弃设计时和编译时的好处。
  • 不要使用允许您仅使用它们来声明属性的模式,例如强类型类。