Comment dynamiser une nouvelle classe anonyme?

95

En C # 3.0, vous pouvez créer une classe anonyme avec la syntaxe suivante

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

Existe-t-il un moyen de créer dynamiquement ces classes anonymes dans une variable?


Exemple:

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

Exemple de création dynamique:

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

Beacuse je dois faire:

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

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

si j'appelle:

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

Il ne peut pas afficher le résultat:

Name = 123

Et aussi comment convertir ExpandoObject en AnonymouseType?

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

Enfin, je modifie la méthode 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));
    } 
  }
}

Comment implémenter la méthode DynamicNewAnonymous ou comment modifier la ShowPropertiesValue ()?

Mes motivations sont:

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

Si je peux accrocher la méthode GetType de dynamicObject et forcer la conversion en type fortement typé. Le code Seamless ci-dessus peut fonctionner correctement.

Éclat
la source
@Vlad: J'avoue que je ne suis pas certain des motivations.
Steven Sudit
@VladLazarenko Je pense que vous avez raison :-)
oberfreak
Veuillez nous dire ce que vous voulez faire et pourquoi c'est la solution que vous avez choisie.
oberfreak
ExpandoObject, pas ExpandObject (ajouté «o»).
N0thing
@StevenSudit Cet article peut vous aider à connaître votre motivation à utiliser l'un ou l'autre: blogs.msdn.com/b/csharpfaq/archive/2010/01/25/…
juagicre

Réponses:

75

Les types anonymes ne sont que des types réguliers qui sont déclarés implicitement. Ils ont peu à voir avec dynamic.

Maintenant, si vous deviez utiliser un ExpandoObject et le référencer via une dynamicvariable, vous pouvez ajouter ou supprimer des champs à la volée.

Éditer

Bien sûr que vous pouvez: il suffit de le lancer IDictionary<string, object>. Ensuite, vous pouvez utiliser l'indexeur.

Vous utilisez la même technique de diffusion pour parcourir les champs:

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

Le code ci-dessus et plus peuvent être trouvés en cliquant sur ce lien.

Steven Sudit
la source
1
Mais ExpandoObject ne peut pas le faire:dynamic o1 = new ExpandObject(); o1."ID" = 1; o1."Name" = "Foo";
Flash
Mais il ne peut pas non plus faire: Tapez o1Type = o1.GetType (); var props = o1Type.GetProperties (); props est vide
Flash
3
Tout ce que vous faites, c'est dire que les propriétés dynamiques ne sont pas identiques aux propriétés fortement typées. C'est trivialement vrai.
Steven Sudit
4
stackoverflow.com/a/4024786/998793 montre comment faire cela en jetant un dictionnaire générique: ((IDictionary<string, object>)o1).Add("Name", "Foo");. Vous pouvez ensuite accéder en tant queo1.Name
rogersillito
15

Vous pouvez créer un ExpandoObject comme ceci:

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

Et après la conversion en dynamique, ces valeurs ressembleront à des propriétés:

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

Cependant, ce ne sont pas des propriétés réelles et ne sont pas accessibles à l'aide de Reflection. Ainsi, l'instruction suivante renverra un null:

d.GetType().GetProperty("Name") 
Daniel
la source
2

Bien sûr, il est possible de créer des classes dynamiques en utilisant la classe ExpandoObject très cool. Mais récemment, j'ai travaillé sur un projet et j'ai fait face à ce que Expando Object n'est pas réalisé dans le même format sur xml qu'une simple classe Anonymous, c'était dommage = (, c'est pourquoi j'ai décidé de créer ma propre classe et de la partager avec vous. réflexion et directive dynamique, construit l'assemblage, la classe et l'instance de manière vraiment dynamique. Vous pouvez ajouter, supprimer et modifier à la volée les propriétés incluses dans votre classe. Voici:

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

    }
}

Il est également très simple d'utiliser DynamicTypeBuilder, il vous suffit de mettre quelques lignes comme ceci:

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

    }

J'espère que cet extrait de code aidera quelqu'un =) Profitez-en!

Svetlana Gurskaya
la source