我不确定我的问题的标题是否有意义。但基本上,我想在泛型集合中实现层次结构,但是我不想调用泛型方法或索引器,而是希望将子项命名为可访问的。很像,如果你愿意,一个完全合格的名字。
就结构而言,以下是我要创建的示例:
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。
如果这不可能,我会坚持课堂宣言,但如果有更好的方法,我真的想学习如何做。
答案 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
并接受可变性,同时放弃设计时和编译时的好处。