web-dev-qa-db-fra.com

Comment créer un délégué pour une propriété .NET?

J'essaie de créer un délégué (à titre de test) pour:

Public Overridable ReadOnly Property PropertyName() As String

Ma tentative intuitive consistait à déclarer le délégué comme ceci:

Public Delegate Function Test() As String

Et instanciant comme ça:

Dim t As Test = AddressOf e.PropertyName

Mais cela jette l'erreur:

Méthode 'La propriété ReadOnly publique Overridable' PropertyName () As String 'n'a pas de signature compatible avec le délégué' Delegate Function Test () As String '.

Donc, comme je traitais avec une propriété, j'ai essayé ceci:

Public Delegate Property Test() As String

Mais cela jette une erreur de compilation.

La question est donc, comment puis-je faire un délégué pour une propriété?


Voir ce lien:

http://peisker.net/dotnet/propertydelegates.htm

20
Matt Mitchell

Problème lié à l'utilisation de AddressOf - si vous connaissez le nom de propriété au moment de la compilation, vous pouvez (au moins en C #) utiliser une méthode anon/lambda:

Test t = delegate { return e.PropertyName; }; // C# 2.0
Test t = () => e.PropertyName; // C# 3.0

Je ne suis pas un VB expert, mais le réflecteur dit que c'est la même chose que:

Dim t As Test = Function 
    Return e.PropertyName
End Function

Ça marche?


Réponse originale:

Vous créez des délégués pour les propriétés avec Delegate.CreateDelegate; cela peut être ouvert pour n'importe quelle instance du type, fixe pour une seule instance - et peut être pour getter ou setter; Je vais donner un exemple en C # ...

using System;
using System.Reflection;
class Foo
{
    public string Bar { get; set; }
}
class Program
{
    static void Main()
    {
        PropertyInfo prop = typeof(Foo).GetProperty("Bar");
        Foo foo = new Foo();

        // create an open "getter" delegate
        Func<Foo, string> getForAnyFoo = (Func<Foo, string>)
            Delegate.CreateDelegate(typeof(Func<Foo, string>), null,
                prop.GetGetMethod());

        Func<string> getForFixedFoo = (Func<string>)
            Delegate.CreateDelegate(typeof(Func<string>), foo,
                prop.GetGetMethod());

        Action<Foo,string> setForAnyFoo = (Action<Foo,string>)
            Delegate.CreateDelegate(typeof(Action<Foo, string>), null,
                prop.GetSetMethod());

        Action<string> setForFixedFoo = (Action<string>)
            Delegate.CreateDelegate(typeof(Action<string>), foo,
                prop.GetSetMethod());

        setForAnyFoo(foo, "abc");
        Console.WriteLine(getForAnyFoo(foo));
        setForFixedFoo("def");
        Console.WriteLine(getForFixedFoo());
    }
}
36
Marc Gravell

Je viens de créer une assistance avec de très bonnes performances: http://thibaud60.blogspot.com/2010/10/fast-property-accessor-without-dynamic.html Elle n'utilise pas l'approche IL/Emit et elle c'est très rapide!

Édité par oscilatingcretin 2015/10/23

La source contient des problèmes de boîtier et un ="" particulier qui doivent être supprimés. Avant que la pourriture des liens ne s'installe, je pensais publier une version nettoyée de la source pour une copie facile des pâtes, ainsi qu'un exemple d'utilisation.

Source révisée

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace Tools.Reflection
{
    public interface IPropertyAccessor
    {
        PropertyInfo PropertyInfo { get; }
        object GetValue(object source);
        void SetValue(object source, object value);
    }

    public static class PropertyInfoHelper
    {
        private static ConcurrentDictionary<PropertyInfo, IPropertyAccessor> _cache =
            new ConcurrentDictionary<PropertyInfo, IPropertyAccessor>();

        public static IPropertyAccessor GetAccessor(PropertyInfo propertyInfo)
        {
            IPropertyAccessor result = null;
            if (!_cache.TryGetValue(propertyInfo, out result))
            {
                result = CreateAccessor(propertyInfo);
                _cache.TryAdd(propertyInfo, result); ;
            }
            return result;
        }

        public static IPropertyAccessor CreateAccessor(PropertyInfo PropertyInfo)
        {
            var GenType = typeof(PropertyWrapper<,>)
                .MakeGenericType(PropertyInfo.DeclaringType, PropertyInfo.PropertyType);
            return (IPropertyAccessor)Activator.CreateInstance(GenType, PropertyInfo);
        }
    }

    internal class PropertyWrapper<TObject, TValue> : IPropertyAccessor where TObject : class
    {
        private Func<TObject, TValue> Getter;
        private Action<TObject, TValue> Setter;

        public PropertyWrapper(PropertyInfo PropertyInfo)
        {
            this.PropertyInfo = PropertyInfo;

            MethodInfo GetterInfo = PropertyInfo.GetGetMethod(true);
            MethodInfo SetterInfo = PropertyInfo.GetSetMethod(true);

            Getter = (Func<TObject, TValue>)Delegate.CreateDelegate
                    (typeof(Func<TObject, TValue>), GetterInfo);
            Setter = (Action<TObject, TValue>)Delegate.CreateDelegate
                    (typeof(Action<TObject, TValue>), SetterInfo);
        }

        object IPropertyAccessor.GetValue(object source)
        {
            return Getter(source as TObject);
        }

        void IPropertyAccessor.SetValue(object source, object value)
        {
            Setter(source as TObject, (TValue)value);
        }

        public PropertyInfo PropertyInfo { get; private set; }
    }
}

Utilisez-le comme ceci:

public class MyClass
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

MyClass e = new MyClass();
IPropertyAccessor[] Accessors = e.GetType().GetProperties()
    .Select(pi => PropertyInfoHelper.CreateAccessor(pi)).ToArray();

foreach (var Accessor in Accessors)
{
    Type pt = Accessor.PropertyInfo.PropertyType;
    if (pt == typeof(string))
        Accessor.SetValue(e, Guid.NewGuid().ToString("n").Substring(0, 9));
    else if (pt == typeof(int))
        Accessor.SetValue(e, new Random().Next(0, int.MaxValue));

    Console.WriteLine(string.Format("{0}:{1}",
        Accessor.PropertyInfo.Name, Accessor.GetValue(e)));
}
10
user478450

Voici une version C #/.NET 2.0 de Réponse de Marc Gravell :

using System;
using System.Reflection;

class Program
{
 private delegate void SetValue<T>(T value);
 private delegate T GetValue<T>();

 private class Foo
 {
  private string _bar;

  public string Bar
  {
   get { return _bar; }
   set { _bar = value; }
  }
 }

 static void Main()
 {
  Foo foo = new Foo();
  Type type = typeof (Foo);
  PropertyInfo property = type.GetProperty("Bar");

  // setter
  MethodInfo methodInfo = property.GetSetMethod();
  SetValue<string> setValue =
   (SetValue<string>) Delegate.CreateDelegate(typeof (SetValue<string>), foo, methodInfo);
  setValue("abc");

  // getter
  methodInfo = property.GetGetMethod();
  GetValue<string> getValue =
   (GetValue<string>) Delegate.CreateDelegate(typeof (GetValue<string>), foo, methodInfo);
  string myValue = getValue();

  // output results
  Console.WriteLine(myValue);
 }
}

Encore une fois, ' Delegate.CreateDelegate ' est la base de cet exemple.

4
Ray Vega

C'est une bonne idée

Test t = () => e.PropertyName; // C# 3.0

Mais faites attention si vous faites quelque chose comme ça:

List<Func<int>> funcs = new List<Func<int>>();
foreach (var e in Collection)
   funcs.Add(new Func<int>(() => e.Property));

Appelant ceci:

foreach(var f in funcs)
   f();

Renverra toujours la valeur de la propriété de last object dans Collection

Dans ce cas, vous devriez appeler la méthode:

foreach (var e in Collection)
   funcs.Add(new Func<int>(e.GetPropValue));
2
vajanko

Version VB:

Dim prop As PropertyInfo = GetType(foo).GetProperty("bar")
Dim foo1 As New foo

Dim getForAnyFoo As Func(Of foo, String) = TryCast([Delegate].CreateDelegate(GetType(Func(Of foo, String)), Nothing, prop.GetGetMethod()), Func(Of foo, String))

Dim setForAnyFoo As Action(Of foo, String) = TryCast([Delegate].CreateDelegate(GetType(Action(Of foo, String)), Nothing, prop.GetSetMethod()), Action(Of foo, String))

Dim getForFixedFoo As Func(Of String) = TryCast([Delegate].CreateDelegate(GetType(Func(Of String)), foo1, prop.GetGetMethod()), Func(Of String))

Dim setForFixedFoo As Action(Of String) = TryCast([Delegate].CreateDelegate(GetType(Action(Of String)), foo1, prop.GetSetMethod()), Action(Of String))

    setForAnyFoo(foo1, "abc")
    Debug.WriteLine(getForAnyFoo(foo1))

    setForFixedFoo("def")
    Debug.WriteLine(getForFixedFoo())
0
stentor

Voici un exemple en C # mais tous les types sont les mêmes:

Commencez par créer l'interface (délégué). N'oubliez pas qu'une méthode que vous attachez à votre délégué doit renvoyer le même type et prendre les mêmes paramètres que la déclaration de votre délégué. Ne définissez pas votre délégué dans la même portée que votre événement.

public delegate void delgJournalBaseModified();        

Créez un événement basé sur le délégué:

public static class JournalBase {
    public static event delgJournalBaseModified evntJournalModified;
};

Définissez une méthode pouvant être liée à votre événement avec une interface identique à celle du délégué.

void UpdateEntryList()
{
}

Nouez la méthode à l'événement. La méthode est appelée lorsque l'événement est déclenché. Vous pouvez lier autant de méthodes à votre événement. Je ne connais pas la limite. C'est probablement quelque chose de fou.

 JournalBase.evntJournalModified += new delgJournalBaseModified(UpdateEntryList);

Qu'est-ce qui se passe ici est que la méthode est ajoutée en tant que rappel pour votre événement. Lorsque l'événement est déclenché, votre méthode sera appelée.

Ensuite, nous créons une méthode qui déclenchera l’événement quand il sera appelé

public static class JournalBase {
    public static  void JournalBase_Modified()
    {
    if (evntJournalModified != null)
        evntJournalModified();
    }
};

Ensuite, vous appelez simplement la méthode - JournalBase_Modified () - quelque part dans votre code et toutes les méthodes liées à votre événement sont également appelées l'une après l'autre.

0
Will