2015-04-02 61 views
18

我试图将字典转换为匿名类型,每个键都有一个属性。来自辞典属性的C#匿名对象

我试过谷歌它,但我能找到的是如何将匿名对象转换为字典。

我的字典里看起来是这样的:

var dict = new Dictionary<string, string> 
{ 
    {"Id", "1"}, 
    {"Title", "My title"}, 
    {"Description", "Blah blah blah"}, 
}; 

,我想返回一个匿名对象,看起来像这样。

var o = new 
{ 
    Id = "1", 
    Title = "My title", 
    Description = "Blah blah blah" 
}; 

所以我会把它想环通每keyValuePair在字典中和每一个键的对象创建属性。

我不知道从哪里开始。

请帮忙。

+7

你使用匿名对象是什么? C#不是JavaScript。 – ryanyuyu 2015-04-02 13:18:46

+1

我不确定这是可能的,因为你在这里提出的对象在编译时生成一个类,所以你不能在运行时创建属性。 – 2015-04-02 13:19:39

+1

匿名类型不是一个动态类型:看看这个问题:http://stackoverflow.com/questions/391529/anonymous-type-vs-dynamic-type – Martijn 2015-04-02 13:24:08

回答

25

基本上,你不能。匿名类型由编译器创建,因此它们存在于程序集中,并且所有属性名称都被烘焙到它们中。 (该物业类型不是在这种情况下一个问题 - 作为一个实现细节,编译器创建一个泛型类型,然后创建的,使用适当的类型参数的实例。)

你问一个类型在执行时确定的属性时间 - 这不符合匿名类型的工作方式。你将不得不在基本编译代码时使用它 - 这会很痛苦,因为它会在不同的程序集中,并且匿名类型是内部的...

也许你应该使用ExpandoObject而不是?然后任何使用dynamic的东西都可以正常访问属性。

+2

他仍然可以通过反射动态地创建一个新类型。我不*说*这将是有用的或智能的......这似乎是一个XY问题 – xanatos 2015-04-02 13:20:36

+1

@xanatos他不能动态地创建一个新的匿名类型。这必须由编译器在编译时生成。是的气味像XY问题。 – 2015-04-02 13:21:37

+1

@SriramSakthivel“anonymous”的概念就是“你不用C#编写代码,编译器用一个有趣的名字和一些属性来生成它”......他肯定可以创建一个新类型,给它一个有趣的名称和一些属性......从技术上讲,它不会是一个匿名类型,但差异很小 – xanatos 2015-04-02 13:22:57

2

这里是你如何能做到这一点作为一个ExpandoObject(帮助从this post

var dict = new Dictionary<string, string> 
{ 
    {"Id", "1"}, 
    {"Title", "My title"}, 
    {"Description", "Blah blah blah"}, 
}; 

var expando = new ExpandoObject() as IDictionary<string, Object>; 
foreach(var kvp in dict) 
    expando.Add(kvp.Key, kvp.Value); 
3

基本上,这是不是你可以做的平凡;匿名类型仍然拥有幕后的所有常用代码:只是你没有看到它。你可以做的是通过dynamic做同样的事情,但是这对于期望反射等的API不起作用 - 所以假设你必须通过键访问,对于大多数代码字典访问将比匿名类型或dynamic类型。

1

这不是一个匿名对象,但你可以用DynamicObject来完成!你可以做以下事情。

public class MyExpando : DynamicObject 
{ 
    Dictionary<string, object> dictionary; 
    public MyExpando() 
    { 
     dictionary = new Dictionary<string, object> 
     { 
      {"Id", "1"}, 
      {"Title", "My title"}, 
      {"Description", "Blah blah blah"}, 
     }; 
    } 

    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
     return dictionary.TryGetValue(binder.Name, out result); 
    } 

    public override bool TrySetMember(SetMemberBinder binder, object value) 
    { 
     if (!dictionary.ContainsKey(binder.Name)) 
      return false; 
     dictionary[binder.Name] = value; 
     return true; 
    } 
} 

然后你有你想要的属性的对象。

dynamic o = new MyExpando(); 
Console.WriteLine("Id : " + o.Id + ", Title : " + o.Title + ", Description : " + o.Description); 

当然,编译时没有类型检查,也没有在编译时检查成员是否存在。

9

现在,至少每月一次有人问如何在运行时创建一个匿名类型...这里是回应:

using System; 
using System.Collections.Concurrent; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using System.Reflection; 
using System.Reflection.Emit; 
using System.Runtime.CompilerServices; 
using System.Text; 
using System.Threading; 

/// <summary> 
/// The code generated should be nearly equal to the one generated by 
/// csc 12.0.31101.0 when compiling with /optimize+ /debug-. The main 
/// difference is in the GetHashCode() (the base init_hash used is 
/// compiler-dependant) and in the maxstack of the generated methods. 
/// Note that Roslyn (at least the one present at 
/// tryroslyn.azurewebsites.net) generates different code for anonymous 
/// types. 
/// </summary> 
public static class AnonymousType 
{ 
    private static readonly ConcurrentDictionary<string, Type> GeneratedTypes = new ConcurrentDictionary<string, Type>(); 

    private static readonly AssemblyBuilder AssemblyBuilder; 
    private static readonly ModuleBuilder ModuleBuilder; 
    private static readonly string FileName; 

    // Some objects we cache 
    private static readonly CustomAttributeBuilder CompilerGeneratedAttributeBuilder = new CustomAttributeBuilder(typeof(CompilerGeneratedAttribute).GetConstructor(Type.EmptyTypes), new object[0]); 
    private static readonly CustomAttributeBuilder DebuggerBrowsableAttributeBuilder = new CustomAttributeBuilder(typeof(DebuggerBrowsableAttribute).GetConstructor(new[] { typeof(DebuggerBrowsableState) }), new object[] { DebuggerBrowsableState.Never }); 
    private static readonly CustomAttributeBuilder DebuggerHiddenAttributeBuilder = new CustomAttributeBuilder(typeof(DebuggerHiddenAttribute).GetConstructor(Type.EmptyTypes), new object[0]); 

    private static readonly ConstructorInfo ObjectCtor = typeof(object).GetConstructor(Type.EmptyTypes); 
    private static readonly MethodInfo ObjectToString = typeof(object).GetMethod("ToString", BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null); 

    private static readonly ConstructorInfo StringBuilderCtor = typeof(StringBuilder).GetConstructor(Type.EmptyTypes); 
    private static readonly MethodInfo StringBuilderAppendString = typeof(StringBuilder).GetMethod("Append", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(string) }, null); 
    private static readonly MethodInfo StringBuilderAppendObject = typeof(StringBuilder).GetMethod("Append", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(object) }, null); 

    private static readonly Type EqualityComparer = typeof(EqualityComparer<>); 
    private static readonly Type EqualityComparerGenericArgument = EqualityComparer.GetGenericArguments()[0]; 
    private static readonly MethodInfo EqualityComparerDefault = EqualityComparer.GetMethod("get_Default", BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null); 
    private static readonly MethodInfo EqualityComparerEquals = EqualityComparer.GetMethod("Equals", BindingFlags.Instance | BindingFlags.Public, null, new[] { EqualityComparerGenericArgument, EqualityComparerGenericArgument }, null); 
    private static readonly MethodInfo EqualityComparerGetHashCode = EqualityComparer.GetMethod("GetHashCode", BindingFlags.Instance | BindingFlags.Public, null, new[] { EqualityComparerGenericArgument }, null); 

    private static int Index = -1; 

    static AnonymousType() 
    { 
     var assemblyName = new AssemblyName("AnonymousTypes"); 

     FileName = assemblyName.Name + ".dll"; 

     AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); 
     ModuleBuilder = AssemblyBuilder.DefineDynamicModule("AnonymousTypes", FileName); 
    } 

    public static void Dump() 
    { 
     AssemblyBuilder.Save(FileName); 
    } 

    /// <summary> 
    /// 
    /// </summary> 
    /// <param name="types"></param> 
    /// <param name="names"></param> 
    /// <returns></returns> 
    public static Type CreateType(Type[] types, string[] names) 
    { 
     if (types == null) 
     { 
      throw new ArgumentNullException("types"); 
     } 

     if (names == null) 
     { 
      throw new ArgumentNullException("names"); 
     } 

     if (types.Length != names.Length) 
     { 
      throw new ArgumentException("names"); 
     } 

     // Anonymous classes are generics based. The generic classes 
     // are distinguished by number of parameters and name of 
     // parameters. The specific types of the parameters are the 
     // generic arguments. We recreate this by creating a fullName 
     // composed of all the property names, separated by a "|" 
     string fullName = string.Join("|", names.Select(x => Escape(x))); 

     Type type; 

     if (!GeneratedTypes.TryGetValue(fullName, out type)) 
     { 
      // We create only a single class at a time, through this lock 
      // Note that this is a variant of the double-checked locking. 
      // It is safe because we are using a thread safe class. 
      lock (GeneratedTypes) 
      { 
       if (!GeneratedTypes.TryGetValue(fullName, out type)) 
       { 
        int index = Interlocked.Increment(ref Index); 

        string name = names.Length != 0 ? string.Format("<>f__AnonymousType{0}`{1}", index, names.Length) : string.Format("<>f__AnonymousType{0}", index); 
        TypeBuilder tb = ModuleBuilder.DefineType(name, TypeAttributes.AnsiClass | TypeAttributes.Class | TypeAttributes.AutoLayout | TypeAttributes.NotPublic | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit); 
        tb.SetCustomAttribute(CompilerGeneratedAttributeBuilder); 

        GenericTypeParameterBuilder[] generics = null; 

        if (names.Length != 0) 
        { 
         string[] genericNames = Array.ConvertAll(names, x => string.Format("<{0}>j__TPar", x)); 
         generics = tb.DefineGenericParameters(genericNames); 
        } 
        else 
        { 
         generics = new GenericTypeParameterBuilder[0]; 
        } 

        // .ctor 
        ConstructorBuilder constructor = tb.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig, CallingConventions.HasThis, generics); 
        constructor.SetCustomAttribute(DebuggerHiddenAttributeBuilder); 
        ILGenerator ilgeneratorConstructor = constructor.GetILGenerator(); 
        ilgeneratorConstructor.Emit(OpCodes.Ldarg_0); 
        ilgeneratorConstructor.Emit(OpCodes.Call, ObjectCtor); 

        var fields = new FieldBuilder[names.Length]; 

        // There are two for cycles because we want to have 
        // all the getter methods before all the other 
        // methods 
        for (int i = 0; i < names.Length; i++) 
        { 
         // field 
         fields[i] = tb.DefineField(string.Format("<{0}>i__Field", names[i]), generics[i], FieldAttributes.Private | FieldAttributes.InitOnly); 
         fields[i].SetCustomAttribute(DebuggerBrowsableAttributeBuilder); 

         // .ctor 
         constructor.DefineParameter(i + 1, ParameterAttributes.None, names[i]); 
         ilgeneratorConstructor.Emit(OpCodes.Ldarg_0); 

         if (i == 0) 
         { 
          ilgeneratorConstructor.Emit(OpCodes.Ldarg_1); 
         } 
         else if (i == 1) 
         { 
          ilgeneratorConstructor.Emit(OpCodes.Ldarg_2); 
         } 
         else if (i == 2) 
         { 
          ilgeneratorConstructor.Emit(OpCodes.Ldarg_3); 
         } 
         else if (i < 255) 
         { 
          ilgeneratorConstructor.Emit(OpCodes.Ldarg_S, (byte)(i + 1)); 
         } 
         else 
         { 
          // Ldarg uses a ushort, but the Emit only 
          // accepts short, so we use a unchecked(...), 
          // cast to short and let the CLR interpret it 
          // as ushort 
          ilgeneratorConstructor.Emit(OpCodes.Ldarg, unchecked((short)(i + 1))); 
         } 

         ilgeneratorConstructor.Emit(OpCodes.Stfld, fields[i]); 

         // getter 
         MethodBuilder getter = tb.DefineMethod(string.Format("get_{0}", names[i]), MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, CallingConventions.HasThis, generics[i], Type.EmptyTypes); 
         ILGenerator ilgeneratorGetter = getter.GetILGenerator(); 
         ilgeneratorGetter.Emit(OpCodes.Ldarg_0); 
         ilgeneratorGetter.Emit(OpCodes.Ldfld, fields[i]); 
         ilgeneratorGetter.Emit(OpCodes.Ret); 

         PropertyBuilder property = tb.DefineProperty(names[i], PropertyAttributes.None, CallingConventions.HasThis, generics[i], Type.EmptyTypes); 
         property.SetGetMethod(getter); 
        } 

        // ToString() 
        MethodBuilder toString = tb.DefineMethod("ToString", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, CallingConventions.HasThis, typeof(string), Type.EmptyTypes); 
        toString.SetCustomAttribute(DebuggerHiddenAttributeBuilder); 
        ILGenerator ilgeneratorToString = toString.GetILGenerator(); 

        ilgeneratorToString.DeclareLocal(typeof(StringBuilder)); 

        ilgeneratorToString.Emit(OpCodes.Newobj, StringBuilderCtor); 
        ilgeneratorToString.Emit(OpCodes.Stloc_0); 

        // Equals 
        MethodBuilder equals = tb.DefineMethod("Equals", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, CallingConventions.HasThis, typeof(bool), new[] { typeof(object) }); 
        equals.SetCustomAttribute(DebuggerHiddenAttributeBuilder); 
        equals.DefineParameter(1, ParameterAttributes.None, "value"); 
        ILGenerator ilgeneratorEquals = equals.GetILGenerator(); 
        ilgeneratorEquals.DeclareLocal(tb); 

        ilgeneratorEquals.Emit(OpCodes.Ldarg_1); 
        ilgeneratorEquals.Emit(OpCodes.Isinst, tb); 
        ilgeneratorEquals.Emit(OpCodes.Stloc_0); 
        ilgeneratorEquals.Emit(OpCodes.Ldloc_0); 

        Label equalsLabel = ilgeneratorEquals.DefineLabel(); 

        // GetHashCode() 
        MethodBuilder getHashCode = tb.DefineMethod("GetHashCode", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, CallingConventions.HasThis, typeof(int), Type.EmptyTypes); 
        getHashCode.SetCustomAttribute(DebuggerHiddenAttributeBuilder); 
        ILGenerator ilgeneratorGetHashCode = getHashCode.GetILGenerator(); 
        ilgeneratorGetHashCode.DeclareLocal(typeof(int)); 

        if (names.Length == 0) 
        { 
         ilgeneratorGetHashCode.Emit(OpCodes.Ldc_I4_0); 
        } 
        else 
        { 
         // As done by Roslyn 
         // Note that initHash can vary, because 
         // string.GetHashCode() isn't "stable" for 
         // different compilation of the code 
         int initHash = 0; 

         for (int i = 0; i < names.Length; i++) 
         { 
          initHash = unchecked(initHash * (-1521134295) + fields[i].Name.GetHashCode()); 
         } 

         // Note that the CSC seems to generate a 
         // different seed for every anonymous class 
         ilgeneratorGetHashCode.Emit(OpCodes.Ldc_I4, initHash); 
        } 

        for (int i = 0; i < names.Length; i++) 
        { 
         // Equals() 
         Type equalityComparerT = EqualityComparer.MakeGenericType(generics[i]); 
         MethodInfo equalityComparerTDefault = TypeBuilder.GetMethod(equalityComparerT, EqualityComparerDefault); 
         MethodInfo equalityComparerTEquals = TypeBuilder.GetMethod(equalityComparerT, EqualityComparerEquals); 

         ilgeneratorEquals.Emit(OpCodes.Brfalse_S, equalsLabel); 
         ilgeneratorEquals.Emit(OpCodes.Call, equalityComparerTDefault); 
         ilgeneratorEquals.Emit(OpCodes.Ldarg_0); 
         ilgeneratorEquals.Emit(OpCodes.Ldfld, fields[i]); 
         ilgeneratorEquals.Emit(OpCodes.Ldloc_0); 
         ilgeneratorEquals.Emit(OpCodes.Ldfld, fields[i]); 
         ilgeneratorEquals.Emit(OpCodes.Callvirt, equalityComparerTEquals); 

         // GetHashCode(); 
         MethodInfo EqualityComparerTGetHashCode = TypeBuilder.GetMethod(equalityComparerT, EqualityComparerGetHashCode); 

         ilgeneratorGetHashCode.Emit(OpCodes.Stloc_0); 
         ilgeneratorGetHashCode.Emit(OpCodes.Ldc_I4, -1521134295); 
         ilgeneratorGetHashCode.Emit(OpCodes.Ldloc_0); 
         ilgeneratorGetHashCode.Emit(OpCodes.Mul); 
         ilgeneratorGetHashCode.Emit(OpCodes.Call, EqualityComparerDefault); 
         ilgeneratorGetHashCode.Emit(OpCodes.Ldarg_0); 
         ilgeneratorGetHashCode.Emit(OpCodes.Ldfld, fields[i]); 
         ilgeneratorGetHashCode.Emit(OpCodes.Callvirt, EqualityComparerGetHashCode); 
         ilgeneratorGetHashCode.Emit(OpCodes.Add); 

         // ToString() 
         ilgeneratorToString.Emit(OpCodes.Ldloc_0); 
         ilgeneratorToString.Emit(OpCodes.Ldstr, i == 0 ? string.Format("{{ {0} = ", names[i]) : string.Format(", {0} = ", names[i])); 
         ilgeneratorToString.Emit(OpCodes.Callvirt, StringBuilderAppendString); 
         ilgeneratorToString.Emit(OpCodes.Pop); 
         ilgeneratorToString.Emit(OpCodes.Ldloc_0); 
         ilgeneratorToString.Emit(OpCodes.Ldarg_0); 
         ilgeneratorToString.Emit(OpCodes.Ldfld, fields[i]); 
         ilgeneratorToString.Emit(OpCodes.Box, generics[i]); 
         ilgeneratorToString.Emit(OpCodes.Callvirt, StringBuilderAppendObject); 
         ilgeneratorToString.Emit(OpCodes.Pop); 
        } 

        // .ctor 
        ilgeneratorConstructor.Emit(OpCodes.Ret); 

        // Equals() 
        if (names.Length == 0) 
        { 
         ilgeneratorEquals.Emit(OpCodes.Ldnull); 
         ilgeneratorEquals.Emit(OpCodes.Ceq); 
         ilgeneratorEquals.Emit(OpCodes.Ldc_I4_0); 
         ilgeneratorEquals.Emit(OpCodes.Ceq); 
        } 
        else 
        { 
         ilgeneratorEquals.Emit(OpCodes.Ret); 
         ilgeneratorEquals.MarkLabel(equalsLabel); 
         ilgeneratorEquals.Emit(OpCodes.Ldc_I4_0); 
        } 

        ilgeneratorEquals.Emit(OpCodes.Ret); 

        // GetHashCode() 
        ilgeneratorGetHashCode.Emit(OpCodes.Stloc_0); 
        ilgeneratorGetHashCode.Emit(OpCodes.Ldloc_0); 
        ilgeneratorGetHashCode.Emit(OpCodes.Ret); 

        // ToString() 
        ilgeneratorToString.Emit(OpCodes.Ldloc_0); 
        ilgeneratorToString.Emit(OpCodes.Ldstr, names.Length == 0 ? "{ }" : " }"); 
        ilgeneratorToString.Emit(OpCodes.Callvirt, StringBuilderAppendString); 
        ilgeneratorToString.Emit(OpCodes.Pop); 
        ilgeneratorToString.Emit(OpCodes.Ldloc_0); 
        ilgeneratorToString.Emit(OpCodes.Callvirt, ObjectToString); 
        ilgeneratorToString.Emit(OpCodes.Ret); 

        type = tb.CreateType(); 

        type = GeneratedTypes.GetOrAdd(fullName, type); 
       } 
      } 
     } 

     if (types.Length != 0) 
     { 
      type = type.MakeGenericType(types); 
     } 

     return type; 
    } 

    private static string Escape(string str) 
    { 
     // We escape the \ with \\, so that we can safely escape the 
     // "|" (that we use as a separator) with "\|" 
     str = str.Replace(@"\", @"\\"); 
     str = str.Replace(@"|", @"\|"); 
     return str; 
    } 
} 

这又大又肥的代码块将使用Reflection.Emit在运行时产生的一些班级,不过你想要的(你甚至可以使用在C#中非法字符)命名的属性。这些类将遵循由C#编译器12.0.31101.0生成的匿名类的命名约定。即使在IL级别,它们也应该几乎相等。所有各种各样的Equals(),GetHashCode()ToString()被实现为由C#编译器12.0.31101.0实现,所有字段都具有相同的名称,CompilerGeneratedAttribute被插入(这在道德上是错误的,我知道:-))等等。所以这些课程生成了walk like an anonymous duck和庸医,就像一个匿名的鸭子......他们是实际上匿名的鸭子:-)(请注意,罗斯林鸭子是一个不同的亚种...他们的羽毛有点不同...一个匿名鸭connaisseur可以容易区分两个子种类:-))

现在,如果你想问cui prodest?生成的类只能通过反射来使用,所以它们很难使用,但它们有一个特定的用例,Marc Gravell个性化:.NET框架的一部分大量使用反射来渲染对象(例如所有的各种数据网格)。这些部件与dynamic对象不兼容,并且通常不支持object[]。一个解决办法往往是封装在一个DataTable数据...或者你可以使用这个:-)

在特定情况下,你可以使用这个方法:

public static object FromDictToAnonymousObj<TValue>(IDictionary<string, TValue> dict) 
{ 
    var types = new Type[dict.Count]; 

    for (int i = 0; i < types.Length; i++) 
    { 
     types[i] = typeof(TValue); 
    } 

    // dictionaries don't have an order, so we force an order based 
    // on the Key 
    var ordered = dict.OrderBy(x => x.Key).ToArray(); 

    string[] names = Array.ConvertAll(ordered, x => x.Key); 

    Type type = AnonymousType.CreateType(types, names); 

    object[] values = Array.ConvertAll(ordered, x => (object)x.Value); 

    object obj = type.GetConstructor(types).Invoke(values); 

    return obj; 
} 

这样的:

var dict = new Dictionary<string, string> 
{ 
    {"Id", "1"}, 
    {"Title", "My title"}, 
    {"Description", "Blah blah blah"}, 
}; 

object obj1 = FromDictToAnonymousObj(dict); 

获取您的对象。

+0

这应该被接受回答 – 2015-10-02 07:02:10

+2

更新版本可以在https://github.com/StefH/System .Linq.Dynamic.Core/blob/master/src/System.Linq.Dynamic.Core/DynamicClassFactory.cs – 2016-05-15 09:04:56

+0

不错,但我做了一个更有限的版本,但我希望它合并匹配类型,如编译器。 – NetMage 2018-01-05 23:00:19