【问题标题】:A dictionary where value is an anonymous type in C#C# 中值是匿名类型的字典
【发布时间】:2009-10-24 23:43:18
【问题描述】:

是否可以在 C# 中创建一个 System.Collections.Generic.Dictionary<TKey, TValue>,其中 TKey 是无条件类,而 TValue - 具有许多属性的匿名类,例如 - 数据库列名称和本地化名称。

类似这样的:

new { ID = 1, Name = new { Column = "Dollar", Localized = "Доллар" } }

【问题讨论】:

  • 非常相似(使用 linq):A generic list of anonymous class
  • .Select(...).AsEnumerable().ToDictionary(k => k.id, v => v as object) 为我工作。我的变量是 Dictionary
  • @Ravishankar:你很可能不需要AsEnumerable()。它不会在 Select() 之上添加任何内容。
  • @abatishchev。真的。我想说“作为对象”让我的生活更轻松。其余代码可以忽略。
  • @Ravi object 不是匿名类型。

标签: c# .net linq dictionary anonymous-types


【解决方案1】:

您不能直接声明这样的字典类型(有些杂乱无章,但这些仅用于娱乐和新奇目的),但如果您的数据来自 IEnumerableIQueryable 源,您可以使用LINQ ToDictionary() 运算符并从序列元素中投射出所需的键和(匿名键入的)值:

var intToAnon = sourceSequence.ToDictionary(
    e => e.Id,
    e => new { e.Column, e.Localized });

【讨论】:

    【解决方案2】:

    作为itowlsonsaid,你不能声明这样一只野兽,但你确实可以创造一只:

    static IDictionary<TKey, TValue> NewDictionary<TKey, TValue>(TKey key, TValue value)
    {
        return new Dictionary<TKey, TValue>();
    }
    
    static void Main(string[] args)
    {
        var dict = NewDictionary(new {ID = 1}, new { Column = "Dollar", Localized = "Доллар" });
    }
    

    不清楚你为什么要使用这样的代码。

    【讨论】:

    • 为什么?我能想到几个原因。这是一个。例如,考虑一个用于记忆 n 元函数的字典,比如一个有四个 int 参数的函数。在 CLR 的下一个版本中,您当然只使用 4 元组,但在 C# 3 中,您可以创建匿名类型 {int, int, int, int} 的字典,完成,无需定义自己的元组类型。
    • 不清楚你为什么想要这个?复合键怎么样(例如 new { State = "NY", Country = "US" })
    • 或引用类型中的复合结果(新的内联元组是值类型)。
    【解决方案3】:

    我认为 ASP.NET MVC 在提出这个问题时并未退出。它确实在内部将匿名对象转换为字典。

    例如,看看HtmlHelper class。将对象转换为字典的方法是AnonymousObjectToHtmlAttributes。但是,它是 MVC 特有的并返回 RouteValueDictionary

    如果你想要更通用的东西,试试这个:

    public static IDictionary<string,object> AnonymousObjectToDictionary(object obj)
    {
        return TypeDescriptor.GetProperties(obj)
            .OfType<PropertyDescriptor>()
            .ToDictionary(
                prop => prop.Name,
                prop => prop.GetValue(obj)
            );
    }
    

    此实现的一个有趣的优点是它为null 对象返回一个空字典。

    这是一个通用版本:

    public static IDictionary<string,T> AnonymousObjectToDictionary<T>(
        object obj, Func<object,T> valueSelect
    )
    {
        return TypeDescriptor.GetProperties(obj)
            .OfType<PropertyDescriptor>()
            .ToDictionary<PropertyDescriptor,string,T>(
                prop => prop.Name,
                prop => valueSelect(prop.GetValue(obj))
            );
    }
    

    【讨论】:

    • 感谢您告知!我可以说这种方法的缺点是使用反射。
    • @abatishchev 我认为没有反思就不可能解决问题。我不确定,但使用 TypeDescriptor 读取属性可能比使用常规 .GetType().GetProperties() 更有效。顺便说一句,我在 HtmlHelper 上使用了 ILSpy,它的作用几乎完全相同。
    • object 不是匿名类型。这与我们想要的相反。 -- 最终结果是一个不包含匿名类型的强类型字典。
    【解决方案4】:

    你可以做一个反思

    public static class ObjectExtensions
    {
        /// <summary>
        /// Turn anonymous object to dictionary
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static IDictionary<string, object> ToDictionary(this object data)
        {
            var attr = BindingFlags.Public | BindingFlags.Instance;
            var dict = new Dictionary<string, object>();
            foreach (var property in data.GetType().GetProperties(attr))
            {
                if (property.CanRead)
                {
                    dict.Add(property.Name, property.GetValue(data, null));
                }
            }
            return dict;
        }
    }
    

    【讨论】:

    • 好主意。你也可以让它通用:public static IDictionary&lt;string, T&gt; ToDictionary(this object data) { }
    【解决方案5】:

    如果你想初始化一个空字典,你可以这样做:

    var emptyDict = Enumerable
        .Empty<(int, string)>()
        .ToDictionary(
             x => new { Id = x.Item1 }, 
             x => new { Column = x.Item2, Localized = x.Item2});
    

    基本上,您只需要一个带有元组的空枚举,该元组具有您要在最终匿名类型中使用的类型,然后您可以获得一个按您喜欢的方式键入的空字典。

    如果你愿意,你也可以命名元组中的类型:

    var emptyDict = Enumerable
                .Empty<(int anInt, string aString)>()
                .ToDictionary(
                    x => new { Id = x.anInt },
                    x => new { Column = x.aString, Localized = x.aString});
    

    【讨论】:

    • 元组是不必要的,并且会产生对元组的错误依赖感。这里的关键技巧是调用扩展方法ToDictionary(),但这可以使用任何空枚举来完成,然后将字面值分配给匿名类型以进行类型推断。像这样:var emptyDictx = Enumerable.Empty&lt;object&gt;().ToDictionary(_ =&gt; 0, _ =&gt; new { Column = string.Empty, Localized = string.Empty }); 请注意,即使是 Lamba 参数也可以丢弃。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多