Bagaimana cara dinamis Kelas Anonim baru?

95

Di C # 3.0 Anda dapat membuat kelas anonim dengan sintaks berikut

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

Apakah ada cara untuk membuat kelas anonim ini dinamis menjadi variabel?


Contoh:

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

Contoh pembuatan dinamis:

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

Karena saya perlu melakukan:

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

Dan Scene1:

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

jika saya menelepon:

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

Itu tidak dapat menunjukkan hasil:

Name = 123

Dan juga bagaimana cara Mengonversi ExpandoObject menjadi AnonymouseType?

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

Terakhir, saya memodifikasi metode ShowPropertiesValue ()

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

Bagaimana cara menerapkan metode DynamicNewAnonymous atau cara memodifikasi ShowPropertiesValue ()?

Motivasi saya adalah:

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

Jika saya dapat mengaitkan Metode GetType dynamicObject, dan Memaksa ubah ke Jenis yang diketik dengan kuat. Kode Seamless di atas dapat berfungsi dengan baik.

Flash
sumber
@Vlad: Saya akui saya agak tidak jelas tentang motivasinya.
Steven Sudit
@VladLazarenko Saya pikir Anda benar :-)
oberfreak
Tolong beritahu kami, apa yang ingin Anda lakukan dan mengapa ini adalah solusi pilihan Anda.
oberfreak
ExpandoObject, bukan ExpandObject (ditambahkan 'o').
N0thing
@StevenSudit Artikel ini dapat membantu untuk mengetahui motivasi Anda menggunakan satu atau yang lain: blogs.msdn.com/b/csharpfaq/archive/2010/01/25/...
juagicre

Jawaban:

75

Tipe anonim hanyalah tipe biasa yang dideklarasikan secara implisit. Mereka tidak ada hubungannya dengan dynamic.

Sekarang, jika Anda menggunakan ExpandoObject dan mereferensikannya melalui dynamicvariabel, Anda dapat menambahkan atau menghapus bidang dengan cepat.

edit

Tentu Anda bisa: cukup transmisikan ke IDictionary<string, object>. Kemudian Anda dapat menggunakan pengindeks.

Anda menggunakan teknik pengecoran yang sama untuk mengulangi bidang:

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

Kode di atas dan lebih banyak lagi dapat ditemukan dengan mengklik tautan itu.

Steven Sudit
sumber
1
Tetapi ExpandoObject tidak dapat melakukannya:dynamic o1 = new ExpandObject(); o1."ID" = 1; o1."Name" = "Foo";
Flash
Tapi itu juga tidak bisa melakukan: Type o1Type = o1.GetType (); var props = o1Type.GetProperties (); alat peraga kosong
Flash
3
Yang Anda lakukan hanyalah mengatakan bahwa properti dinamis tidak identik dengan properti yang diketik dengan kuat. Sepele ini benar.
Steven Sudit
4
stackoverflow.com/a/4024786/998793 menunjukkan bagaimana melakukan ini dengan casting untuk sebuah kamus generik: ((IDictionary<string, object>)o1).Add("Name", "Foo");. Anda kemudian dapat mengakses sebagaio1.Name
rogersillito
15

Anda dapat membuat ExpandoObject seperti ini:

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

Dan setelah mentransmisikannya menjadi dinamis, nilai-nilai itu akan terlihat seperti properti:

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

Namun, ini bukan properti sebenarnya dan tidak dapat diakses menggunakan Refleksi. Jadi pernyataan berikut akan mengembalikan null:

d.GetType().GetProperty("Name") 
Daniel
sumber
2

Karena itu dimungkinkan untuk membuat kelas dinamis menggunakan kelas ExpandoObject yang sangat keren. Tapi baru-baru ini saya mengerjakan proyek dan menghadapi bahwa Expando Object adalah serealized dalam format yang tidak sama pada xml sebagai kelas Anonymous sederhana, itu kasihan = (itulah mengapa saya memutuskan untuk membuat kelas saya sendiri dan membaginya dengan Anda. refleksi dan direktif dinamis, membangun Majelis, Kelas dan Mesin Virtual benar-benar dinamis.Anda dapat menambah, menghapus dan mengubah properti yang termasuk dalam kelas Anda dengan cepat Ini dia:

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

    }
}

Menggunakan DynamicTypeBuilder juga sangat mudah, Anda hanya perlu meletakkan beberapa baris seperti ini:

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

    }

Saya harap potongan kode ini membantu seseorang =) Selamat menikmati!

Svetlana Gurskaya
sumber