将 Dictionary<string, object> 转换为匿名对象?

100

首先,为了让事情更清晰,我将从头开始解释我的场景:

我有一个方法,它具有以下签名:

public virtual void SendEmail(String from, List<String> recepients, Object model)
我想做的是生成一个匿名对象,该对象具有模型对象的属性以及前两个参数。将模型对象转换为PropertyInfo[]非常简单。因此,我考虑创建一个Dictionary,其中包含PropertyInfo和前两个参数,然后将其转换为匿名对象,其中键是属性名称,值是属性的实际值。
这可能吗?还有其他建议吗?

你想做这件事的原因是什么? - Mattias Jakobsson
1
我怀疑您能够轻松地支持任意的键值对 - 您必须在运行时动态构造具有这些属性的新类型。由于您只会读取它们,因此最好创建一个重载函数,也接受您的字典。 - Rup
@Rup:实际上,那也是一个合理的替代方案。我已经找到了一个适合我的要求的快捷方式,但我仍然想知道上面问题的答案...只是出于好奇 :) - Kassem
请查看以下链接,这是将字典转换为匿名类型的非常好的解决方案:https://jacobcarpenter.wordpress.com/2008/03/13/dictionary-to-anonymous-type/https://tomsundev.wordpress.com/2011/07/20/create-an-anonymoustype-from-a-dictionary/ - Mrinal Kamboj
9个回答

140

如果你真的想将字典转换为具有字典项属性的对象,可以使用ExpandoObject

var dict = new Dictionary<string, object> { { "Property", "foo" } };
var eo = new ExpandoObject();
var eoColl = (ICollection<KeyValuePair<string, object>>)eo;

foreach (var kvp in dict)
{
    eoColl.Add(kvp);
}

dynamic eoDynamic = eo;

string value = eoDynamic.Property;

3
+1但我不确定那样做会如何帮助你。 - secretAgentB
如何在vb.net中做同样的事情? - Mojtaba Rezaeian
3
为什么要这样做呢?当你向 Razor 模板传递一个模型时,使用动态对象比使用字典更容易操作。所以,如果你有一个字典需要将它转换为动态对象。然后,在 *.cshtml 模板中,占位符应该像这样写:@Model.Name,而不是这样写:@Model["Name"]。 - dalenewman
3
副笔:ExpandoObject 使用反射不起作用,因为它不是真正的对象,所以如果你恰好将结果传递给需要反射一个对象的东西,那么这并不能解决问题。你的选择似乎是花哨的动态类型创建(也许可以参考 https://benohead.com/blog/2013/12/26/create-anonymous-types-at-runtime-in-c-sharp/),或者……找到 / 开发一个不需要能够被反射的对象的新 API。 - bambams
"System.Dynamic.ExpandoObject"没有包含名为"Property"的定义。 - sergiol
显示剩余2条评论

38

我尝试使用reduce函数(C#中的Aggregate)在一条语句中完成此操作。下面的代码与被接受的答案执行相同的操作:

var dict = new Dictionary<string, object> { { "Property", "foo" } };
dynamic eo = dict.Aggregate(new ExpandoObject() as IDictionary<string, Object>,
                            (a, p) => { a.Add(p); return a; });
string value = eo.Property;

14

如果您想将 Dictionary<string, object> 转换为匿名的 System.Object,您可以使用以下方法:

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);

获取您的对象。 其中AnonymousType类代码如下:

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;
    }
}

参考资料:https://dev59.com/mV0b5IYBdhLWcg3wQPRN#29428640

这篇文章提供了一种在Java中使用SHA-256算法进行哈希的方法。它解释了如何使用Java内置的MessageDigest类并逐步说明了如何对输入数据应用哈希功能。此外,文章还介绍了BASE64编码和字节转换的相关知识,并给出了示例代码以便理解和实践。

在DotNet Core中是否有匿名类型的实现? - Cruiser KID
@CruiserKID 我不知道有任何类似的实现。尽管你可以在 .NET Core 中创建和操作匿名类型,就像在 .NET 中一样。 - Abdul Rauf

12
如果您有一个想要将字典转换为的类,可以使用以下内容将字典转换为该类的对象:
示例类:
public class Properties1
{
    public string Property { get; set; }
}

解决方案:
JavaScriptSerializer serializer = new JavaScriptSerializer();
Dictionary<string, object> dict = new Dictionary<string, object> { { "Property", "foo" } };
Properties1 properties = serializer.ConvertToType<Properties1>(dict);
string value = properties.Property;

您还可以使用类似于以下这种方法从字典构建对象,当然这也需要您拥有一个类。
private static T DictionaryToObject<T>(IDictionary<string, object> dict) where T : new()
{
    T t = new T();
    PropertyInfo[] properties = t.GetType().GetProperties();

    foreach (PropertyInfo property in properties)
    {
        if (!dict.Any(x => x.Key.Equals(property.Name, 
            StringComparison.InvariantCultureIgnoreCase)))
            continue;
        KeyValuePair<string, object> item = dict.First(x => x.Key.Equals(property.Name,
            StringComparison.InvariantCultureIgnoreCase));
        Type tPropertyType = t.GetType().GetProperty(property.Name).PropertyType;
        Type newT = Nullable.GetUnderlyingType(tPropertyType) ?? tPropertyType;
        object newA = Convert.ChangeType(item.Value, newT);
        t.GetType().GetProperty(property.Name).SetValue(t, newA, null);
    }
    return t;
}

然而,如果您没有该类,可以通过以下方式从字典创建动态对象:
private static dynamic DictionaryToObject(Dictionary<string, object> dict)
{
    IDictionary<string, object> eo = (IDictionary<string, object>)new ExpandoObject();
    foreach (KeyValuePair<string, object> kvp in dict)
    {
        eo.Add(kvp);
    }
    return eo;
}

您可以像这样使用它:
Dictionary<string, object> dict = new Dictionary<string, object> {{ "Property", "foo" }};
dynamic properties = DictionaryToObject(dict);
string value = properties.Property;

9

这是svick答案的稍微模块化版本,使用了一些扩展方法:

public static class Extensions
{
    public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> items)
    {
        foreach (var item in items)
        {
            collection.Add(item);
        }
    }

    public static dynamic ToDynamicObject(this IDictionary<string, object> source)
    {
        ICollection<KeyValuePair<string, object>> someObject = new ExpandoObject();
        someObject.AddRange(source);
        return someObject;
    }
}

1
我喜欢这个想法。不过,没有“ICollection.AddRange”。你必须要么通过使用扩展提供AddRange,要么尝试使用“source.ToList().ForEach(someObject.Add)”。 - Nils
糟糕,好发现!我太习惯于使用扩展方法了,以至于把它们当作理所当然。会更新答案。 - Geir Sagberg

4
以下代码处理子字典并将它们转换为嵌套动态对象:
[return: NotNullIfNotNull(nameof(dictionary))]
static dynamic? ToDynamic(IReadOnlyDictionary<string, object?>? dictionary) =>
    dictionary?.Aggregate(
        (IDictionary<string, object?>)new ExpandoObject(),
        (obj, i) =>
        {
            if (i.Value is IReadOnlyDictionary<string, object?> nestedDictionary)
                obj.Add(new(i.Key, ToDynamic(nestedDictionary)));
            else
                obj.Add(i);
            return obj;
        });

这种方法允许您通过访问动态对象本身来访问嵌套字典的内容:

var record = ToDynamic(...);
string cityName = record.city.name;

3

匿名对象是由编译器为您生成的对象。您无法动态地创建一个。另一方面,您可以发出这样的对象,但我真的不认为这是一个好主意。

也许你可以尝试动态对象?结果将是一个拥有所有所需属性的对象。


1
动态对象会很完美。能否提供一个例子? - Kassem
1
我认为这不是一个准确的说法,你甚至可以在运行时创建真正的CLR类型,我无法想象为什么你不能在运行时创建匿名类型。 - Chris Marisic
3
“@ChrisMarisic”的回答中说:“你可以发出这样的对象”。当然可以。 - Mike Chaliy
6
这不是答案,而是一条评论。 - JotaBe
@JotaBe,实际上这是一个回答,间接地表达了“你不能这样做”的意思。我认为这不是正确的答案,但它仍然是一个答案。 - theta-fish

3

这里要感谢被接受的答案。我想把一个List< Dictionary< string,object >>转换成一个List< dynamic>,目的是从数据库表中获取记录。以下是我的操作步骤。

    public static List<dynamic> ListDictionaryToListDynamic(List<Dictionary<string,object>> dbRecords)
    {
        var eRecords = new List<dynamic>();
        foreach (var record in dbRecords)
        {
            var eRecord = new ExpandoObject() as IDictionary<string, object>;
            foreach (var kvp in record)
            {
                eRecord.Add(kvp);
            }
            eRecords.Add(eRecord);
        }
        return eRecords;
    }

-1
一个通用版本:
public static dynamic ToDynamic<T>(this IDictionary<string, T> source) where T : class
{
    var eo = new ExpandoObject() as IDictionary<string, object>;
    foreach (var kvp in source)
    {
        eo.Add(kvp.Key, kvp.Value);
    }

    return eo;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接