2011-09-06 84 views
12

我要疯了。这可能是因为我错过了一些规则,但如果是这样,那么请告诉我。字典与对象作为价值

我试图创建一个Dictionary与一个字符串的键和一个匿名对象作为值。或者,其实我不只是在尝试,我正在做。 但是当我想改变对象中的特定参数时,它会出错。

我宣布字典是这样的:

var areas = new Dictionary<string, object> 
{ 
    {"Key1", new {name = "Name1", today = 0, all = 0}}, 
    {"Key2", new {name = "Name2", today = 0, all = 0}}, 
    ... 
} 

然后我假设我可以这样做:

foreach (var area in areas.Keys.ToArray()) 
{ 
    var areaname = areas[area].name; 
} 

但Visual Studio的不同意,并拒绝编译。不过,如果我写这篇文章,一切都按我的想法工作 - 但这并不能真正帮助我,这只会让我更加沮丧。

var areaname = new 
      { 
       name = "Nametest", today = 0, all = 0 
      }; 
var testname = areaname.name; 

我在做什么错?为什么它不适合我?这样做根本不可能吗?

感谢您的所有答案,他们肯定会清理一些东西! 我想我可能一直在混淆类型对象与对象的思想,即类,在C#中的挫败感。我会重新考虑整个设计业务,可能会做一些完全不同的事情,而不是使用邪恶或肮脏的解决方案。虽然有趣,但我不认为我与C#的关系已经发展到如此遥远!

+0

您是否尝试过使用“动态”而不是你的值类型的“对象”? –

+1

编译器无法解析从字典中检索的对象(值)上的Name属性。对于var的情况,编译器会放入真正的on-the-fly类型来代替var,协助编译器。为什么在这种情况下需要匿名类型?当你知道什么属性应该存在于所有的值,即它不是未知的。 – Gishu

回答

23

这不起作用,因为您已经声明字典为Dictionary<string, object>

你可以改为Dictionary<string, dynamic>

9

这不会编译,因为您的字典中的值是object类型,并且object不包含属性name

如果您使用.NET 4,则可以将类型从object更改为dynamic

说了这话之后,我强烈反对这个设计决定。你真的应该为此创建一个类。

+5

是的 - 不能同意更多 –

1

匿名类型的细节是不可移植的超出他们创造了组装,而不是容易†超出方法他们在创建可见。您应该改用一个onymous类型,或dynamic如果你想采用动态的方法。

†这里有一个邪恶的伎俩,我故意没有提到,因为它是邪恶的。

+2

+1使用单词不可靠 –

+2

匿名类型实际上被记录为结构等同于*在整个程序集*中定义它们,而不仅仅是在方法中。另外,虽然“暧昧型”很可爱,但“指定型”更好。我有时使用“名义类型”,尽管它有不幸的贬义含义,即“只有名字的类型”而不是“具有特定名称的类型”。 –

8

这不起作用,因为您已将该字典声明为Dictionary<string, object>

C#在后台为您创建一个新类型(您无法访问)具有这些属性(以下称为匿名类型)。您可以直接使用它的唯一时间是当它仍然是var类型时 - 只要将它添加到该字典中,它就会被转换为object,因此您无法再访问这些属性。

选项1

你可以代之以Dictionary<string, dynamic>。动力将能够访问这些属性 - 但这只是在C#/支持.NET 4.0

选项2

可以回用被称为演示的技术(举例投)获取类型。这不是一个记录的功能,是浪费,有点破解。 This is a horrible evil trick - 特别是因为它不能跨组件工作。

public static T Demonstrate<T>(this T demonstration, object value) 
{ 
    return (T)value; 
} 

然后,您可以访问原始的属性,像这样:

var area = new { name = "", today = 0, all = 0 }.Demonstrate(areas[name]); 
var areaName = value.name; 

选项3这是诚实的最佳方式

编写类。如果您喜欢C#编译器能够完成所有EqualsGetHashCodeToString这一事实,那么您可以使用类似ILSpy的东西来获取原始代码。因此:

class AreaInfo 
{ 
    public string Name { get; set; } 
    public int Total { get; set; } 
    public int All { get; set; } 

    public override bool Equals(object obj) 
    { 
     var ai = obj as AreaInfo; 
     if (object.ReferenceEquals(ai, null)) 
      return false; 
     return Name == ai.Name && Total == ai.Total && All == ai.All; 
    } 

    public override int GetHashCode() 
    { 
     var hc = 0; 
     if (Name != null) 
      hc = Name.GetHashCode(); 
     hc = unchecked((hc * 7)^Total); 
     hc = unchecked((hc * 21)^All); 
     return hc; 
    } 

    public override string ToString() 
    { 
     return string.Format("{{ Name = {0}, Total = {1}, All = {2} }}", Name, Total, All); 
    } 
} 

纠正你的词典:

var areas = new Dictionary<string, AreaInfo>  
{  
    {"Key1", new AreaInfo() {Name = "Name1", Today = 0, All = 0}},  
    {"Key2", new AreaInfo() {Name = "Name2", Today = 0, All = 0}},  
    ...  
}; 

现在事情会工作,你希望他们的方式。

+8

您的“演示”方法(通常也称为“CastByExample”)并不可怕,因为它可能会在未来版本的语言中崩溃;它是完全合法的代码,并且没有实现定义的行为。这应该继续工作永远。然而,这太可怕了,因为它不会在组件*上工作。如果您有由程序集ABC.dll创建的匿名类型的字典,并且程序集XYZ.dll试图通过示例进行转换,则即使匿名类型具有相同的“形状”,转换也会失败。 *匿名类型在创建它们的程序集中仅相同。* –

+0

@Eric - 我知道你在为MSFT工作:),但它不属于无证行为的境界吗? –

+3

@Jonathan我去了并检查了C#3.0规范7.5.10.6:“在同一个程序中,两个匿名对象初始值设定项指定相同名称和编译时间类型的属性序列,相同的匿名类型。“ – AakashM

6

为了让您的字典有适当的匿名类型,它的价值,这样做:

var areas=new[] { 
    new {Key="Key1", Value=new {name="Name1", today=0, all=0}}, 
    new {Key="Key2", Value=new {name="Name2", today=0, all=0}}, 
}.ToDictionary(kv => kv.Key, kv => kv.Value); 

那么你的代码将如预期:

foreach(var area in areas.Keys.ToArray()) { 
    var areaname=areas[area].name; 
} 
+0

认真的最好的解决方案在这里.. – nawfal

+0

@nawfal - 我同意这是OP的问题最接近的答案,但我不知道如果为了可读性的代码它不会更好地创建一个实际的类(即使通常我会尽量避免创建一个仅用作“临时”数据容器的类) – BornToCode

+1

@BornToCode你是对的。使用实际的命名类型最有可能是这里要做的最好的事情。我应该说它是最酷的解决方案(鉴于OP对于匿名类型是僵化的):) – nawfal