2011-09-06 65 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