Làm cách nào để tạo Lớp ẩn danh mới?


95

Trong C # 3.0, bạn có thể tạo lớp ẩn danh với cú pháp sau

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

Có cách nào để tạo động các lớp ẩn danh này thành một biến không?


Thí dụ:

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

Tạo động Ví dụ:

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

Báo hiệu tôi cần làm:

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

Và cảnh 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));
  }
}

nếu tôi gọi:

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

Nó không thể hiển thị kết quả:

Name = 123

Và tôi cũng làm cách nào để chuyển đổi ExpandoObject thành AnonymouseType?

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

Cuối cùng, tôi sửa đổi phương thức 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));
    } 
  }
}

Làm cách nào để triển khai phương thức DynamicNewAnonymous hoặc cách sửa đổi ShowPropertiesValue ()?

Động lực của tôi là:

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

Nếu tôi có thể hook Phương thức GetType của dynamicObject và Compel chuyển đổi sang Kiểu được đánh mạnh. Mã Dàn ở trên có thể hoạt động tốt.


@Vlad: Tôi thừa nhận rằng tôi hơi không rõ ràng về động cơ.
Steven Sudit

@VladLazarenko Tôi nghĩ bạn đúng :-)
oberfreak

Vui lòng cho chúng tôi biết bạn muốn làm gì và tại sao đây là giải pháp bạn đã chọn.
oberfreak

ExpandoObject, không phải ExpandObject (thêm 'o').
N0thing

@StevenSudit Bài viết này có thể giúp tìm hiểu động lực của bạn sử dụng một hay khác: blogs.msdn.com/b/csharpfaq/archive/2010/01/25/...
juagicre

Câu trả lời:


75

Các kiểu ẩn danh chỉ là các kiểu thông thường được khai báo ngầm. Chúng có rất ít liên quan dynamic.

Bây giờ, nếu bạn đang sử dụng ExpandoObject và tham chiếu nó thông qua một dynamicbiến, bạn có thể thêm hoặc xóa các trường một cách nhanh chóng.

biên tập

Chắc chắn bạn có thể: chỉ cần truyền nó tới IDictionary<string, object>. Sau đó, bạn có thể sử dụng trình chỉ mục.

Bạn sử dụng cùng một kỹ thuật truyền để lặp qua các trường:

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

Có thể tìm thấy mã trên và nhiều hơn thế nữa bằng cách nhấp vào liên kết đó.


1
Nhưng ExpandoObject không thể làm điều đó:dynamic o1 = new ExpandObject(); o1."ID" = 1; o1."Name" = "Foo";
Flash

Nhưng nó cũng không làm được: Gõ o1Type = o1.GetType (); var props = o1Type.GetProperties (); Đạo cụ trống
Flash

3
Tất cả những gì bạn đang làm là nói rằng các thuộc tính động không giống với các thuộc tính được gõ mạnh. Điều này đúng một cách tầm thường.
Steven Sudit

4
stackoverflow.com/a/4024786/998793 chương trình thế nào để làm điều này bằng cách đúc để một từ điển chung: ((IDictionary<string, object>)o1).Add("Name", "Foo");. Bạn có thể sau đó truy cập nhưo1.Name
rogersillito

15

Bạn có thể tạo một ExpandoObject như sau:

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

Và sau khi truyền nó sang động, những giá trị đó sẽ giống như thuộc tính:

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

Tuy nhiên, chúng không phải là thuộc tính thực tế và không thể truy cập bằng Reflection. Vì vậy, câu lệnh sau sẽ trả về giá trị null:

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

2

Tất nhiên, có thể tạo các lớp động bằng cách sử dụng lớp ExpandoObject rất thú vị. Nhưng gần đây tôi đã làm việc trong dự án và phải đối mặt với việc Đối tượng Expando được sereal hóa không cùng định dạng trên xml với một lớp Ẩn danh đơn giản, thật đáng tiếc = (, đó là lý do tại sao tôi quyết định tạo lớp của riêng mình và chia sẻ nó với bạn. Nó đang sử dụng phản ánh và chỉ thị động, xây dựng Assembly, Class và Instance thực sự năng động. Bạn có thể thêm, xóa và thay đổi các thuộc tính có trong lớp của mình một cách nhanh chóng Đây là:

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

    }
}

Nó cũng rất đơn giản để sử dụng DynamicTypeBuilder, bạn chỉ cần đặt vài dòng như sau:

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

    }

Tôi hy vọng rằng đoạn mã này sẽ giúp được ai đó =) Chúc bạn vui vẻ!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.