Wie kann ich eine neue anonyme Klasse dynamisieren?


95

In C # 3.0 können Sie eine anonyme Klasse mit der folgenden Syntax erstellen

var o1 = new { Id = 1, Name = "Foo" };

Gibt es eine Möglichkeit, diese anonymen Klassen dynamisch für eine Variable zu erstellen?


Beispiel:

var o1 = new { Id = 1, Name = "Foo" };
var o2 = new { SQ = 2, Birth = DateTime.Now };

Dynamisches Erstellen Beispiel:

var o1 = DynamicNewAnonymous(new NameValuePair("Id", 1), new NameValuePair("Name", "Foo"));
var o2 = DynamicNewAnonymous(new NameValuePair("SQ", 2), new NameValuePair("Birth", 
DateTime.Now));

Weil ich tun muss:

dynamic o1 = new ExpandObject(); 
o1."ID" = 1;    <--"ID" is dynamic name
o1."Name" = "Foo";  <--"Name" is dynamic name

Und Szene 1:

void ShowPropertiesValue(object o)
{
  Type oType = o.GetType();
  foreach(var pi in oType.GetProperties())
  {
    Console.WriteLine("{0}={1}", pi.Name, pi.GetValue(o, null));
  }
}

wenn ich anrufe:

dynamic o1 = new ExpandObject();
o1.Name = "123";
ShowPropertiesValue(o1);

Das Ergebnis kann nicht angezeigt werden:

Name = 123

Und wie konvertiere ich das ExpandoObject in AnonymouseType?

Type type = o1.GetType();
type.GetProperties();   <--I hope it can get all property of o1

Zuletzt ändere ich die ShowPropertiesValue () -Methode

void ShowPropertiesValue(object o)
{
  if( o is static object ) <--How to check it is dynamic or static object?
  {
    Type oType = o.GetType();
    foreach(var pi in oType.GetProperties())
    {
      Console.WriteLine("{0}={1}", pi.Name, pi.GetValue(o, null));
    }
  }
  else if( o is dynamic object )  <--How to check it is dynamic or static object?
  {
    foreach(var pi in ??? )  <--How to get common dynamic object's properties info ?
    {
      Console.WriteLine("{0}={1}", pi.Name, pi.GetValue(o, null));
    } 
  }
}

Wie implementiere ich die DynamicNewAnonymous-Methode oder wie ändere ich ShowPropertiesValue ()?

Meine Motivation ist:

dynamic o1 = new MyDynamic();
o1.Name = "abc";
Type o1Type = o1.GetType();
var props = o1Type.GetProperties(); <--I hope can get the Name Property

Wenn ich die GetType-Methode von dynamicObject einbinden und Compel in einen stark typisierten Typ konvertieren kann. Der obige nahtlose Code kann gut funktionieren.


@Vlad: Ich gebe zu, ich bin etwas unklar über die Motivationen.
Steven Sudit

@VladLazarenko Ich denke du hast recht :-)
oberfreak

Bitte teilen Sie uns mit, was Sie tun möchten und warum dies Ihre ausgewählte Lösung ist.
Oberfreak

ExpandoObject, nicht ExpandObject ('o' hinzugefügt).
Nichts

@StevenSudit Dieser Artikel kann helfen, Ihre Motivation für die Verwendung des einen oder anderen herauszufinden
Juagicre

Antworten:


75

Anonyme Typen sind nur reguläre Typen, die implizit deklariert werden. Sie haben wenig damit zu tun dynamic.

Wenn Sie nun ein ExpandoObject verwenden und es über eine dynamicVariable referenzieren , können Sie Felder im laufenden Betrieb hinzufügen oder entfernen.

bearbeiten

Sicher können Sie: werfen Sie es einfach auf IDictionary<string, object>. Dann können Sie den Indexer verwenden.

Sie verwenden dieselbe Casting-Technik, um über die Felder zu iterieren:

dynamic employee = new ExpandoObject();
employee.Name = "John Smith";
employee.Age = 33;

foreach (var property in (IDictionary<string, object>)employee)
{
    Console.WriteLine(property.Key + ": " + property.Value);
}
// This code example produces the following output:
// Name: John Smith
// Age: 33

Den obigen Code und mehr finden Sie, indem Sie auf diesen Link klicken.


1
Aber ExpandoObject kann es nicht tun:dynamic o1 = new ExpandObject(); o1."ID" = 1; o1."Name" = "Foo";
Flash

Das geht aber auch nicht: Typ o1Type = o1.GetType (); var props = o1Type.GetProperties (); Requisiten sind leer
Flash

3
Sie sagen lediglich, dass dynamische Eigenschaften nicht mit stark typisierten Eigenschaften identisch sind. Dies ist trivial wahr.
Steven Sudit

4
stackoverflow.com/a/4024786/998793 zeigt, wie dies durch Casting in ein generisches Wörterbuch funktioniert : ((IDictionary<string, object>)o1).Add("Name", "Foo");. Sie können dann also1.Name
Rogersillito

15

Sie können ein ExpandoObject wie folgt erstellen:

IDictionary<string,object> expando = new ExpandoObject();
expando["Name"] = value;

Und nachdem es in dynamisch umgewandelt wurde, sehen diese Werte wie Eigenschaften aus:

dynamic d = expando;
Console.WriteLine(d.Name);

Sie sind jedoch keine tatsächlichen Eigenschaften und können nicht mit Reflection aufgerufen werden. Die folgende Anweisung gibt also eine Null zurück:

d.GetType().GetProperty("Name") 

1

Natürlich ist es möglich, dynamische Klassen mit einer sehr coolen ExpandoObject-Klasse zu erstellen. Aber kürzlich habe ich an einem Projekt gearbeitet und festgestellt, dass Expando Object in XML nicht im gleichen Format wie eine einfache anonyme Klasse serealisiert ist. Es war schade = (aus diesem Grund habe ich beschlossen, meine eigene Klasse zu erstellen und sie mit Ihnen zu teilen Reflexion und dynamische Direktive, baut Assembly, Klasse und Instanz wirklich dynamisch auf. Sie können Eigenschaften hinzufügen, entfernen und ändern, die in Ihrer Klasse enthalten sind.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using static YourNamespace.DynamicTypeBuilderTest;

namespace YourNamespace
{

    /// This class builds Dynamic Anonymous Classes

    public class DynamicTypeBuilderTest
    {    
        ///   
        /// Create instance based on any Source class as example based on PersonalData
        ///
        public static object CreateAnonymousDynamicInstance(PersonalData personalData, Type dynamicType, List<ClassDescriptorKeyValue> classDescriptionList)
        {
            var obj = Activator.CreateInstance(dynamicType);

            var propInfos = dynamicType.GetProperties();

            classDescriptionList.ForEach(x => SetValueToProperty(obj, propInfos, personalData, x));

            return obj;
        }

        private static void SetValueToProperty(object obj, PropertyInfo[] propInfos, PersonalData aisMessage, ClassDescriptorKeyValue description)
        {
            propInfos.SingleOrDefault(x => x.Name == description.Name)?.SetValue(obj, description.ValueGetter(aisMessage), null);
        }

        public static dynamic CreateAnonymousDynamicType(string entityName, List<ClassDescriptorKeyValue> classDescriptionList)
        {
            AssemblyName asmName = new AssemblyName();
            asmName.Name = $"{entityName}Assembly";
            AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndCollect);

            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule($"{asmName.Name}Module");

            TypeBuilder typeBuilder = moduleBuilder.DefineType($"{entityName}Dynamic", TypeAttributes.Public);

            classDescriptionList.ForEach(x => CreateDynamicProperty(typeBuilder, x));

            return typeBuilder.CreateTypeInfo().AsType();
        }

        private static void CreateDynamicProperty(TypeBuilder typeBuilder, ClassDescriptorKeyValue description)
        {
            CreateDynamicProperty(typeBuilder, description.Name, description.Type);
        }

        ///
        ///Creation Dynamic property (from MSDN) with some Magic
        ///
        public static void CreateDynamicProperty(TypeBuilder typeBuilder, string name, Type propType)
        {
            FieldBuilder fieldBuider = typeBuilder.DefineField($"{name.ToLower()}Field",
                                                            propType,
                                                            FieldAttributes.Private);

            PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(name,
                                                             PropertyAttributes.HasDefault,
                                                             propType,
                                                             null);

            MethodAttributes getSetAttr =
                MethodAttributes.Public | MethodAttributes.SpecialName |
                    MethodAttributes.HideBySig;

            MethodBuilder methodGetBuilder =
                typeBuilder.DefineMethod($"get_{name}",
                                           getSetAttr,
                                           propType,
                                           Type.EmptyTypes);

            ILGenerator methodGetIL = methodGetBuilder.GetILGenerator();

            methodGetIL.Emit(OpCodes.Ldarg_0);
            methodGetIL.Emit(OpCodes.Ldfld, fieldBuider);
            methodGetIL.Emit(OpCodes.Ret);

            MethodBuilder methodSetBuilder =
                typeBuilder.DefineMethod($"set_{name}",
                                           getSetAttr,
                                           null,
                                           new Type[] { propType });

            ILGenerator methodSetIL = methodSetBuilder.GetILGenerator();

            methodSetIL.Emit(OpCodes.Ldarg_0);
            methodSetIL.Emit(OpCodes.Ldarg_1);
            methodSetIL.Emit(OpCodes.Stfld, fieldBuider);
            methodSetIL.Emit(OpCodes.Ret);

            propertyBuilder.SetGetMethod(methodGetBuilder);
            propertyBuilder.SetSetMethod(methodSetBuilder);

        }

        public class ClassDescriptorKeyValue
        {
            public ClassDescriptorKeyValue(string name, Type type, Func<PersonalData, object> valueGetter)
            {
                Name = name;
                ValueGetter = valueGetter;
                Type = type;
            }

            public string Name;
            public Type Type;
            public Func<PersonalData, object> ValueGetter;
        }

        ///
        ///Your Custom class description based on any source class for example
        /// PersonalData
        public static IEnumerable<ClassDescriptorKeyValue> GetAnonymousClassDescription(bool includeAddress, bool includeFacebook)
        {
            yield return new ClassDescriptorKeyValue("Id", typeof(string), x => x.Id);
            yield return new ClassDescriptorKeyValue("Name", typeof(string), x => x.FirstName);
            yield return new ClassDescriptorKeyValue("Surname", typeof(string), x => x.LastName);
            yield return new ClassDescriptorKeyValue("Country", typeof(string), x => x.Country);
            yield return new ClassDescriptorKeyValue("Age", typeof(int?), x => x.Age);
            yield return new ClassDescriptorKeyValue("IsChild", typeof(bool), x => x.Age < 21);

            if (includeAddress)
                yield return new ClassDescriptorKeyValue("Address", typeof(string), x => x?.Contacts["Address"]);
            if (includeFacebook)
                yield return new ClassDescriptorKeyValue("Facebook", typeof(string), x => x?.Contacts["Facebook"]);
        }

        ///
        ///Source Data Class for example
        /// of cause you can use any other class
        public class PersonalData
        { 
            public int Id { get; set; }
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public string Country { get; set; }
            public int Age { get; set; }

            public Dictionary<string, string> Contacts { get; set; }
        }

    }
}

Es ist auch sehr einfach, DynamicTypeBuilder zu verwenden. Sie müssen nur einige Zeilen wie diese einfügen:

    public class ExampleOfUse
    {
        private readonly bool includeAddress;
        private readonly bool includeFacebook;
        private readonly dynamic dynamicType;
        private readonly List<ClassDescriptorKeyValue> classDiscriptionList;
        public ExampleOfUse(bool includeAddress = false, bool includeFacebook = false)
        {
            this.includeAddress = includeAddress;
            this.includeFacebook = includeFacebook;
            this.classDiscriptionList = DynamicTypeBuilderTest.GetAnonymousClassDescription(includeAddress, includeFacebook).ToList();
            this.dynamicType = DynamicTypeBuilderTest.CreateAnonymousDynamicType("VeryPrivateData", this.classDiscriptionList);
        }

        public object Map(PersonalData privateInfo)
        {
            object dynamicObject = DynamicTypeBuilderTest.CreateAnonymousDynamicInstance(privateInfo, this.dynamicType, classDiscriptionList);

            return dynamicObject;
        }

    }

Ich hoffe, dass dieses Code-Snippet jemandem hilft =) Viel Spaß!

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.