Um die Dinge klarer zu machen, erkläre ich zunächst mein Szenario von oben:
Ich habe eine Methode mit der folgenden Signatur:
public virtual void SendEmail(String from, List<String> recepients, Object model)
Ich möchte ein anonymes Objekt generieren, das neben den ersten beiden Parametern auch die Eigenschaften des Modellobjekts aufweist. Das Reduzieren des Modellobjekts in eine PropertyInfo [] ist sehr einfach. Dementsprechend dachte ich daran, ein Wörterbuch zu erstellen, das die PropertyInfos und die ersten beiden Parameter enthält und dann in das anonyme Objekt konvertiert wird, wobei der Schlüssel der Name der Eigenschaft und der Wert der tatsächliche Wert der Eigenschaft ist.
Ist das möglich? Irgendwelche anderen Vorschläge?
c#
.net
dictionary
anonymous-types
Kassem
quelle
quelle
Antworten:
Wenn Sie das Wörterbuch wirklich in ein Objekt konvertieren möchten, dessen Elemente die Elemente des Wörterbuchs enthalten, können Sie Folgendes verwenden
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;
Aber ich bin mir nicht sicher, wie dir das helfen wird.
quelle
ExpandoObject
nicht mit Reflexion, da es sich nicht um ein reales Objekt handelt. Wenn Sie also das Ergebnis an etwas übergeben, für das ein reflektiertes Objekt erforderlich ist, wird das Problem nicht gelöst. Ihre Optionen scheinen eine ausgefallene dynamische Typerstellung zu sein (möglicherweise benohead.com/blog/2013/12/26/… ) oder ... eine neue API zu finden / entwickeln, für die kein Objekt erforderlich ist, über das reflektiert werden kann.Ich habe versucht, dies in einer Anweisung mit einer Reduktionsfunktion (Aggregate in Linq) zu tun. Der folgende Code entspricht der akzeptierten Antwort:
var dict = new Dictionary<string, object> { { "Property", "foo" } }; dynamic eo = dict.Aggregate(new ExpandoObject() as IDictionary<string, Object>, (a, p) => { a.Add(p.Key, p.Value); return a; }); string value = eo.Property;
quelle
Wenn Sie eine Klasse haben, die Sie auch im Wörterbuch verdecken möchten, können Sie ein Wörterbuch wie folgt in ein Objekt dieser Klasse konvertieren:
Beispielklasse:
public class Properties1 { public string Property { get; set; } }
Die Lösung:
JavaScriptSerializer serializer = new JavaScriptSerializer(); Dictionary<string, object> dict = new Dictionary<string, object> { { "Property", "foo" } }; Properties1 properties = serializer.ConvertToType<Properties1>(dict); string value = properties.Property;
Sie können auch eine solche Methode verwenden, um das Objekt aus dem Wörterbuch zu erstellen. Dazu müssen Sie natürlich auch eine Klasse haben.
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; }
Wenn Sie jedoch nicht über die Klasse verfügen, können Sie ein dynamisches Objekt aus einem Wörterbuch wie dem folgenden erstellen:
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; }
Sie können es so verwenden:
Dictionary<string, object> dict = new Dictionary<string, object> {{ "Property", "foo" }}; dynamic properties = DictionaryToObject(dict); string value = properties.Property;
quelle
Wenn Sie
Dictionary<string, object>
zu Anonym konvertieren möchtenSystem.Object
. Sie können diese Methode verwenden: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; }
so was:
var dict = new Dictionary<string, string> { {"Id", "1"}, {"Title", "My title"}, {"Description", "Blah blah blah"}, }; object obj1 = FromDictToAnonymousObj(dict);
um Ihr Objekt zu erhalten. Wo
AnonymousType
Klassencode ist: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; } }
Referenz: https://stackoverflow.com/a/29428640/2073920
quelle
Etwas modularere Version von svicks Antwort mit ein paar Erweiterungsmethoden:
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; } }
quelle
ICollection.AddRange
. Sie müssen entweder AddRange mithilfe von Erweiterungen bereitstellen oder versuchen,source.ToList().ForEach(someObject.Add)
Anonyme Objekte werden vom Compiler für Sie generiert. Sie können keine dynamisch erstellen. Auf der anderen Seite können Sie ein solches Objekt emittieren, aber ich denke wirklich nicht, dass dies eine gute Idee ist.
Vielleicht können Sie dynamische Objekte ausprobieren? Das Ergebnis ist ein Objekt mit allen Eigenschaften, die Sie benötigen.
quelle
Die Gutschrift hier geht an die akzeptierte Antwort. Dies wurde hinzugefügt, weil ich eine List <Dictionary <Zeichenfolge, Objekt >> in eine List <Dynamic> verwandeln wollte. Der Zweck besteht darin, Datensätze aus einer Datenbanktabelle abzurufen. Hier ist was ich getan habe.
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; }
quelle