101 votes

Comment créer une nouvelle classe anonyme dynamique ?

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

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

Y a-t-il un moyen de créer dynamiquement cette classe anonyme vers une variable ?


Exemple :

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

Créer dynamiquement un exemple :

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

Parce que j'ai besoin de faire :

dynamic o1 = new ExpandObject(); 
o1."ID" = 1;    <--"ID" est un nom dynamique
o1."Name" = "Foo";  <--"Nom" est un nom dynamique

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

Si j'appelle :

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

Il ne peut pas afficher le résultat :

Nom = 123

Et aussi comment convertir l'ExpandoObject en AnonymouseType ?

Type type = o1.GetType();
type.GetProperties();   <-- J'espère qu'il peut obtenir toutes les propriétés de o1

Enfin, je modifie la méthode ShowPropertiesValue()

void ShowPropertiesValue(object o)
{
  if( o est un objet statique ) <-- Comment vérifier s'il s'agit d'un objet dynamique ou statique ?
  {
    Type oType = o.GetType();
    foreach(var pi in oType.GetProperties())
    {
      Console.WriteLine("{0}={1}", pi.Name, pi.GetValue(o, null));
    }
  }
  else if( o est un objet dynamique )  <-- Comment vérifier s'il s'agit d'un objet dynamique ou statique ?
  {
    foreach(var pi in ??? )  <-- Comment obtenir les informations sur les propriétés des objets dynamiques communs ?
    {
      Console.WriteLine("{0}={1}", pi.Name, pi.GetValue(o, null));
    } 
  }
}

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

Mes motivations sont :

dynamic o1 = new MyDynamic();
o1.Name = "abc";
Type o1Type = o1.GetType();
var props = o1Type.GetProperties(); <-- J'espère pouvoir obtenir la propriété Nom

Si je peux crocheter la méthode GetType de l'objet dynamique, et forcer la conversion en un Type fortement typé. Le code ci-dessus sans couture peut fonctionner correctement.

80voto

Steven Sudit Points 13793

Les types anonymes ne sont que des types réguliers déclarés implicitement. Ils n'ont pas grand-chose à voir avec dynamic.

Maintenant, si vous deviez utiliser un ExpandoObject et y faire référence via une variable dynamic, vous pourriez ajouter ou supprimer des champs à la volée.

éditer

Vous pouvez bien sûr : il suffit de le caster en IDictionary. Ensuite, vous pouvez utiliser l'indexeur.

Vous utilisez la même technique de casting pour itérer sur les champs :

dynamic employé = new ExpandoObject();
employé.Nom = "Jean Dupont";
employé.Age = 33;

foreach (var propriété in (IDictionary)employé)
{
    Console.WriteLine(propriété.Key + ": " + propriété.Value);
}
// Cet exemple de code produit la sortie suivante :
// Nom: Jean Dupont
// Age: 33

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

17voto

Daniel Points 152

Vous pouvez créer un ExpandoObject de cette manière :

IDictionary expando = new ExpandoObject();
expando["Name"] = value;

Et après l'avoir converti 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 peuvent pas être accessibles en utilisant la Réflexion. Ainsi, l'instruction suivante renverra un null :

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

4voto

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 constaté que l'objet Expando est sérialisé dans un format différent sur XML par rapport à une simple classe anonyme, c'était dommage =( C'est pourquoi j'ai décidé de créer ma propre classe et de la partager avec vous. Elle utilise la réflexion et la directive dynamique, construit une Assembly, une Classe et une Instance de manière vraiment dynamique. Vous pouvez ajouter, supprimer et modifier les propriétés incluses dans votre classe à la volée Voici :

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

namespace YourNamespace
{

    /// Cette classe construit des Classes Anonymes Dynamiques

    public class DynamicTypeBuilderTest
    {    
        ///   
        /// Créer une instance basée sur n'importe quelle classe Source, par exemple basée sur PersonalData
        ///
        public static object CreateAnonymousDynamicInstance(PersonalData personalData, Type dynamicType, List 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 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);
        }

        ///
        ///Création de propriété dynamique (de MSDN) avec un peu de magie
        ///
        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 valueGetter)
            {
                Name = name;
                ValueGetter = valueGetter;
                Type = type;
            }

            public string Name;
            public Type Type;
            public Func ValueGetter;
        }

        ///
        ///Votre description de classe personnalisée basée sur n'importe quelle classe source par exemple
        /// PersonalData
        public static IEnumerable 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"]);
        }

        ///
        ///Classe de données source par exemple
        /// bien sûr, vous pouvez utiliser n'importe quelle autre classe
        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 Contacts { get; set; }
        }

    }
}

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

    public class ExampleOfUse
    {
        private readonly bool includeAddress;
        private readonly bool includeFacebook;
        private readonly dynamic dynamicType;
        private readonly List 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 ce extrait de code aidera quelqu'un =) Profitez-en !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X