2009-04-07 9 views
19

Estoy tratando de crear un delegado (como prueba) para:¿Cómo creo un delegado para una propiedad .NET?

Public Overridable ReadOnly Property PropertyName() As String 

Mi intento intuitiva estaba declarando el delegado de esta manera:

Public Delegate Function Test() As String 

y crear instancias de la siguiente manera:

Dim t As Test = AddressOf e.PropertyName 

Pero esto arroja el error:

Method 'Public Overridable ReadOnly Property PropertyName() As String' does not have a signature compatible with delegate 'Delegate Function Test() As String'.

Así pues estaba tratando con una propiedad He intentado esto:

Public Delegate Property Test() As String 

Pero esto arroja un error de compilación.

Entonces la pregunta es, ¿cómo hago un delegado para una propiedad?


ver este enlace:

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

Respuesta

34

Re el problema utilizando AddressOf - si se conoce el prop-nombre en tiempo de compilación, puede (en C#, por lo menos) utilizar un anon-Método/lambda:

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

no un soy VB experto, pero el reflector afirma que esto es lo mismo que:

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

¿Eso funciona?


Respuesta original:

Se crea delegados para las propiedades con Delegate.CreateDelegate; esto puede estar abierto para cualquier instancia del tipo, de fijo para una sola instancia, y puede ser para getter o setter; Voy a dar un ejemplo 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()); 
    } 
} 
+0

Gracias - Estoy atascado en .NET 2.0 para un proyecto en cuestión y veré si algo similar funciona y comentarios aquí (de lo contrario podría ser la razón por la que surgió la solución elaborada con la que vinculé) –

+0

Parece que funciona (no ha probado extensamente) pero me pregunto si puedes ayudar con este problema. Necesito obtener la propiedad sin usar una cadena codificada. El problema es que necesito el PropertyInfo para obtener el método get y no puedo obtenerlo de la propiedad addressOf –

+0

También gracias por el enfoque PropertyInfo :-) –

0

Aquí está un ejemplo # C, pero todos los tipos son los mismos:

En primer lugar crear la interfaz (delegado). Recuerde, un método que adjunte a su delegado debe devolver el mismo tipo y tomar los mismos parámetros que la declaración de su delegado. No defina su delegado en el mismo ámbito que su evento.

public delegate void delgJournalBaseModified();   

hacer un evento basado en el delegado:

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

definir un método que puede ser atado a su evento que tiene una interfaz idéntica a la del delegado.

void UpdateEntryList() 
{ 
} 

Ate el método al evento. El método se llama cuando se dispara el evento. Puedes vincular tantos métodos a tu evento. No sé el límite. Probablemente sea algo loco.

JournalBase.evntJournalModified += new delgJournalBaseModified(UpdateEntryList); 

Lo que sucede aquí es que el método se agrega como una devolución de llamada para su evento. Cuando se dispara el evento, se llamará a su (s) método (s).

Después un método que va a desencadenar el evento cuando se le llama:

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

Entonces sólo tiene que llamar al método - JournalBase_Modified() - en algún lugar de su código y todos los métodos vinculados a su evento se llama también, uno después de otro.

+0

No te voté por respuesta pero la pregunta se refería a las propiedades –

+1

Sí ... Lo veo después de los hechos. Gracias por no votarme. Parece que respondí sin verificar el contexto de la pregunta ... Tonto. – Will

4

Aquí está una versión de C#/.NET 2.0 de Marc Gravell's response:.

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

Una vez más, 'Delegate.CreateDelegate' es lo que es fundamental a este ejemplo.

10

Acabo de crear un ayudante con un rendimiento bastante bueno: http://thibaud60.blogspot.com/2010/10/fast-property-accessor-without-dynamic.html ¡No utiliza el enfoque IL/Emit y es muy rápido!

Editar por oscilatingcretin 2015/10/23

La fuente contiene algunos problemas de carcasa y peculiar ="" que tienen que ser eliminado. Antes de que se establezca link rot, pensé en publicar una versión limpia de la fuente para copiar fácilmente la pasta, así como un ejemplo de cómo usarla.

fuente revisado

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

utilizar de esta manera:

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))); 
} 
+0

¡Este accesorio de la propiedad suyo es asombroso! Basé mi nueva aplicación en su acceso a la propiedad y después de solucionar algunos errores menores, todo funciona como un encanto. –

+0

En el estado actual, esta no es una respuesta muy buena para SO, y debería ser un comentario o una ampliación ... Basado en (10K +) pregunta eliminada http://stackoverflow.com/questions/33292378/for-generic-parameters pregunta que hay algunas posibilidades de que el código vinculado requiera una nueva versión para poder ser utilizado (y no se puede copiar en SO debido a problemas de licencia). –

+0

@AlexeiLevenkov La pregunta a la que se ha vinculado ha sido eliminada. Además, amplía más las preocupaciones de licencia. El respondedor vinculado a su blog donde dieron un ejemplo de código. Claro, usted no sabe si requiere el uso de licencias, pero ¿cómo sabe que los ejemplos de códigos publicados en SE no requieren licencias? – oscilatingcretin

1

Esta es una buena idea

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

Pero tenga cuidado si su están haciendo algo como esto :

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

Al llamar a este:

foreach(var f in funcs) 
    f(); 

con Gusto valor de la propiedad de la última objeto en la colección

En este caso, debe llamar al método:

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

Versión de 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()) 
Cuestiones relacionadas