字典与对象作为价值

Dum*_*tte 15 c# dictionary object

我在这里疯了.这可能是因为我错过了一些规则,但如果是这样,那么请告诉我.

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

我像这样声明字典:

var areas = new Dictionary<string, object>
{
  {"Key1", new {name = "Name1", today = 0, all = 0}},
  {"Key2", new {name = "Name2", today = 0, all = 0}},
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

foreach (var area in areas.Keys.ToArray())
{
    var areaname = areas[area].name;
}
Run Code Online (Sandbox Code Playgroud)

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

var areaname = new 
            {
                 name = "Nametest", today = 0, all = 0
            };
var testname = areaname.name;
Run Code Online (Sandbox Code Playgroud)

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

谢谢你的所有答案,他们肯定清理了一下!我想我可能一直在混淆类型对象对象的想法,即C#中的类,而不是挫折.我将重新思考整个设计业务,并可能做一些完全不同的事情,而不是使用邪恶或肮脏的解决方案.虽然有趣,但我认为我与C#的关系还没有发展到那么远!

And*_*tan 25

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

你可以尝试Dictionary<string, dynamic>.


Jon*_*son 10

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

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

选项1

你可以尝试Dictionary<string, dynamic>.Dynamic将能够访问这些属性 - 但这仅在C#/ .Net 4.0中受支持

选项2

您可以使用称为演示的技术(通过示例演员)获取类型.这不是一个记录的功能,是浪费,有点像黑客.这是一个可怕的邪恶伎俩 - 特别是因为它不适用于组件.

public static T Demonstrate<T>(this T demonstration, object value)
{
    return (T)value;
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以访问原始属性,如下所示:

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

选项3 这是老实说最好的方法

写一堂课.如果你喜欢的是,C#编译器完成所有的事实Equals,GetHashCodeToString为你工作,你可以使用类似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);
    }
}
Run Code Online (Sandbox Code Playgroud)

纠正你的字典:

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

现在事情会像你期望的那样发挥作用.

  • 你的"演示"方法(通常也称为"CastByExample")并不可怕,因为它可能在未来的语言版本中出现故障; 它是完全合法的代码,并且没有实现定义的行为.这应该会继续下去.但是,这很糟糕,因为*它不会在程序集*之间起作用.如果你有一个由程序集ABC.dll创建的匿名类型的字典,并且程序集XYZ.dll试图通过示例进行转换,即使匿名类型具有相同的"形状",转换也将失败.*匿名类型仅在创建它们的程序集中相同.* (8认同)
  • @Jonathan我去检查了 - C#3.0规范,7.5.10.6:"在同一个程序中,两个匿名对象初始值设定项以相同的顺序指定相同名称和编译时类型的属性序列将生成相同的实例匿名类型." (3认同)

Dan*_*rth 9

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

如果您使用的是.NET 4,则可以将类型更改objectdynamic.

话虽如此,我强烈反对这一设计决定.你真的应该为此创建一个类.

  • 是的 - 不能同意 (5认同)

Cor*_*sak 7

要使Dictionary的值具有正确的匿名类型,请执行以下操作:

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);
Run Code Online (Sandbox Code Playgroud)

然后您的代码将按预期工作:

foreach(var area in areas.Keys.ToArray()) {
  var areaname=areas[area].name;
}
Run Code Online (Sandbox Code Playgroud)