Réflexion

De Banane Atomic
Révision datée du 23 octobre 2017 à 12:09 par Nicolas (discussion | contributions) (→‎Expression Tree à la place de la réflexion)
(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Aller à la navigationAller à la recherche

Utilisation

Pour utiliser GetProperties avec des BindingFlags il faut obligatoirement spécifier BindingFlags.Instance ou BindingFlags.Static.
Csharp.svg
// instanciation dynamique de MaClass
var maClass = (MaClass)Activator.CreateInstance(typeof(MaClass));

// récupération de la valeur du champs statique _staticField
// GetValue(null) doit être utilisé pour obtenir les valeurs des membres statiques
maClass.GetType().GetField("_staticField").GetValue(null);

// récupération de la valeur du champs privé _privateField
// par défaut les champs statiques ne sont pas recherchés, il faut utiliser les BindingFlags
maClass.GetType().GetField("_privateField", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(maClass);

// récupération de la valeur de la propriété PublicProperty
maClass.GetType().GetProperty("PublicProperty").GetValue(maClass, null);

// récupération des propriétés publiques en excluant celles des classes parentes (DeclaredOnly)
foreach (var propertyInfo in maClass.GetType().GetProperties(
    BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
{ ... }

// exécute la méthode PublicMethod
maClass.GetType().GetMethod("PublicMethod").Invoke(maClass, new object[] { 10 }));

// savoir si un type à un constructeur par défaut
if (typeof(MaClass).GetConstructor(Type.EmptyTypes) != null) { }

class MaClass
{
    private int _privateField;
    public static int _staticField = 5;
    public string PublicProperty { get; set; }
    
    public int PublicMethod(int parameter)
    {
        return ++parameter;
    }

    public MaClass()
    {
        _privateField = 1;
    }
}

Types générique

Ce code permet d'instancier un type générique depuis une méthode qui ne l'est pas.

Cs.svg
// récupère le type GenericClass<int>
Type genericType = typeof(GenericClass<>).MakeGenericType(typeof(int));
// instancie le type, 10 est le paramètre passé au constructeur
var genericEvent = Activator.CreateInstance(genericType, 10);

// récupérer le type inclut dans la classe générique
var genericArguments = genericType.GetGenericArguments(); // tableau de types correspondant aux types <T1, T2, ...>
genericArguments[0]; // Int32

class GenericClass<T>
{
    public int i { get; set; }
    public GenericClass(int i) { this.i = i; }
}

Attributs

Obtenir le nom d'une propriété au format String

Csharp.svg
// Méthode 1
// Réflexion sur la classe C1 :
ReflectionUtility.GetPropertyName(() => default(C1).P1);
// Réflexion sur l'objet c1 :
ReflectionUtility.GetPropertyName(() => cc1.P1)

// Méthode 2, réflexion sur la classe C1 sans la fausse erreur de compilation :
ReflectionUtility.GetPropertyName((C1 c1) => c1.P1);

// Méthode 3, extension :
c1.GetPropertyName(() => c1.P1);

public class C1
{
    public int P1 { get; set; }
}

public static class ReflectionUtility
{
    public static string GetPropertyName<T>(Expression<Func<T>> expression)
    {
        return ((MemberExpression)expression.Body).Member.Name;
    }

    public static string GetPropertyName<T, TReturn>(this Expression<Func<T, TReturn>> expression)
    {
        return ((MemberExpression)expression.Body).Member.Name;
    }

    public static string GetPropertyName<Tclass, Tproperty>(this Tclass obj, Expression<Func<Tproperty>> expression) where Tclass : class
    {
        return ((MemberExpression)expression.Body).Member.Name;
    }
}

web site

Obtenir uniquement les propriétés avec un accesseur Set

Csharp.svg
// Ne marche pas
foreach (var prop in this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty))

foreach (var prop in this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
    if (prop.GetSetMethod() != null)

Lister les classes, interfaces, enum d'une assembly

Csharp.svg
var systemAssembly = Assembly.GetAssembly(typeof(Console));

var allSystemTypes = systemAssembly.GetTypes().
    Where(t => t.IsPublic && t.IsClass ||t.IsInterface || t.IsEnum).
    OrderBy(t => t.Name).Select(t => t.Name);

File.AppendAllText("SystemTypes.txt", Regex.Replace(String.Join(" ", allSystemTypes), @"`\d", ""));

Expression Tree à la place de la réflexion

Cs.svg
var mc = new MyClass();
var prop1 = mc.GetType().GetProperty("MyProperty1");

// code utilisant la réflexion
prop1.SetValue(mc, "test");
var v2 = prop1.GetValue(mc);

// code utilisant les Expression, bien plus rapide
var fp = new FastProperty(prop1);
fp.SetDelegate(mc, "test");
var v = fp.GetDelegate(mc);

class FastProperty
{
    public PropertyInfo Property { get; set; }

    public Func<object, object> GetDelegate;

    public Action<object, object> SetDelegate;
        
    public FastProperty(PropertyInfo property)
    {
        this.Property = property;
        InitializeGet();
        InitializeSet();
    }

    private void InitializeSet()
    {
        var instance = Expression.Parameter(typeof(object), "instance");

        var value = Expression.Parameter(typeof(object), "value");

        // value as T is slightly faster than (T)value, so if it's not a value type, use that
        UnaryExpression instanceCast = (!this.Property.DeclaringType.IsValueType) ? 
            Expression.TypeAs(instance, this.Property.DeclaringType) : 
            Expression.Convert(instance, this.Property.DeclaringType);

        UnaryExpression valueCast = (!this.Property.PropertyType.IsValueType) ? 
            Expression.TypeAs(value, this.Property.PropertyType) : 
            Expression.Convert(value, this.Property.PropertyType);

        this.SetDelegate = Expression.Lambda<Action<object, object>>(
            Expression.Call(
                instanceCast,
                this.Property.GetSetMethod(),
                valueCast), 
            new ParameterExpression[] { instance, value }).Compile();
    }

    private void InitializeGet()
    {
        var instance = Expression.Parameter(typeof(object), "instance");

        UnaryExpression instanceCast = (!this.Property.DeclaringType.IsValueType) ? 
            Expression.TypeAs(instance, this.Property.DeclaringType) : 
            Expression.Convert(instance, this.Property.DeclaringType);

        this.GetDelegate = Expression.Lambda<Func<object, object>>(
            Expression.TypeAs(
                Expression.Call(instanceCast, this.Property.GetGetMethod()), 
                typeof(object)), 
            instance).Compile();
    }

    public object Get(object instance)
    {
        return this.GetDelegate(instance);
    }

    public void Set(object instance, object value)
    {
        this.SetDelegate(instance, value);
    }
}