วิธีการไดนามิกคลาส Anonymous ใหม่


95

ใน C # 3.0 คุณสามารถสร้างคลาสที่ไม่ระบุชื่อด้วยไวยากรณ์ต่อไปนี้

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

มีวิธีไดนามิกในการสร้างคลาสที่ไม่ระบุชื่อเหล่านี้ให้กับตัวแปรหรือไม่?


ตัวอย่าง:

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

ตัวอย่างการสร้างแบบไดนามิก:

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

Beacuse ฉันต้องทำ:

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

และ 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));
  }
}

ถ้าฉันโทร:

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

ไม่สามารถแสดงผลลัพธ์:

Name = 123

และฉันจะแปลง ExpandoObject เป็น AnonymouseType ได้อย่างไร

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

สุดท้ายฉันแก้ไข 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));
    } 
  }
}

วิธีการใช้งานเมธอด DynamicNewAnonymous หรือวิธีแก้ไข ShowPropertiesValue ()?

แรงจูงใจของฉันคือ:

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

ถ้าฉันสามารถขอวิธี GetType ของ dynamicObject และ Compel แปลงเป็นชนิดที่พิมพ์มากเกินไป โค้ด Seamless ข้างต้นสามารถทำงานได้ดี


@ วลาด: ฉันยอมรับว่าฉันไม่ค่อยชัดเจนเกี่ยวกับแรงจูงใจ
Steven Sudit

@VladLazarenko ฉันคิดว่าคุณพูดถูก :-)
oberfreak

โปรดบอกเราว่าคุณต้องการทำอะไรและเหตุใดจึงเป็นทางเลือกที่คุณเลือก
oberfreak

ExpandoObject ไม่ใช่ ExpandObject (เพิ่ม 'o')
N0thing

@StevenSudit บทความนี้อาจช่วยค้นหาแรงจูงใจของคุณในการใช้อย่างใดอย่างหนึ่ง: blogs.msdn.com/b/csharpfaq/archive/2010/01/25/…
juagicre

คำตอบ:


75

ประเภทที่ไม่ระบุตัวตนเป็นเพียงประเภทปกติที่มีการประกาศโดยปริยาย dynamicพวกเขามีน้อยจะทำอย่างไรกับ

ตอนนี้ถ้าคุณจะใช้ExpandoObjectและอ้างอิงผ่านdynamicตัวแปรคุณสามารถเพิ่มหรือลบฟิลด์ได้ทันที

แก้ไข

แน่ใจว่าคุณสามารถ: IDictionary<string, object>เพียงแค่โยนมันทิ้งไป จากนั้นคุณสามารถใช้ตัวสร้างดัชนี

คุณใช้เทคนิคการหล่อแบบเดียวกันเพื่อวนซ้ำในฟิลด์:

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

คุณสามารถดูโค้ดด้านบนและอื่น ๆ ได้โดยคลิกที่ลิงค์นั้น


1
แต่ ExpandoObject ไม่สามารถทำได้:dynamic o1 = new ExpandObject(); o1."ID" = 1; o1."Name" = "Foo";
Flash

แต่ก็ไม่สามารถทำได้: พิมพ์ o1Type = o1.GetType (); อุปกรณ์ประกอบฉาก var = o1Type.GetProperties (); อุปกรณ์ประกอบฉากว่างเปล่า
แฟลช

3
สิ่งที่คุณทำคือบอกว่าคุณสมบัติแบบไดนามิกไม่เหมือนกับคุณสมบัติที่พิมพ์อย่างรุนแรง นี่เป็นเรื่องจริงเล็กน้อย
Steven Sudit

4
stackoverflow.com/a/4024786/998793((IDictionary<string, object>)o1).Add("Name", "Foo");แสดงให้เห็นว่าการทำเช่นนี้โดยการหล่อการพจนานุกรมทั่วไป: จากนั้นคุณสามารถเข้าถึงได้ในo1.Name
rogersillito

15

คุณสามารถสร้าง ExpandoObject ได้ดังนี้:

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

และหลังจากแคสต์เป็นไดนามิกแล้วค่าเหล่านั้นจะมีลักษณะเหมือนคุณสมบัติ:

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

อย่างไรก็ตามไม่ใช่คุณสมบัติที่แท้จริงและไม่สามารถเข้าถึงได้โดยใช้ Reflection ดังนั้นคำสั่งต่อไปนี้จะส่งคืนค่าว่าง:

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

2

สาเหตุเป็นไปได้ที่จะสร้างคลาสไดนามิกโดยใช้คลาส ExpandoObject ที่ยอดเยี่ยมมาก แต่เมื่อไม่นานมานี้ฉันได้ทำงานในโครงการและพบว่า Expando Object นั้นถูก serealized ในรูปแบบที่ไม่เหมือนกันบน xml เป็นคลาส Anonymous ธรรมดามันน่าเสียดาย = (นั่นคือเหตุผลที่ฉันตัดสินใจสร้างคลาสของตัวเองและแชร์กับคุณมันใช้ การสะท้อนและคำสั่งแบบไดนามิกสร้างแอสเซมบลีคลาสและอินสแตนซ์แบบไดนามิกอย่างแท้จริงคุณสามารถเพิ่มลบและเปลี่ยนแปลงคุณสมบัติที่รวมอยู่ในคลาสของคุณได้ทันทีนี่คือ:

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

    }
}

นอกจากนี้ยังง่ายมากที่จะใช้ DynamicTypeBuilder คุณต้องใส่ไม่กี่บรรทัดดังนี้:

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

    }

ฉันหวังว่าข้อมูลโค้ดนี้จะช่วยใครสักคน =) สนุก!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.