Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I create a delegate for a .NET property?

Tags:

I am trying to create a delegate (as a test) for:

Public Overridable ReadOnly Property PropertyName() As String 

My intuitive attempt was declaring the delegate like this:

Public Delegate Function Test() As String 

And instantiating like this:

Dim t As Test = AddressOf e.PropertyName 

But this throws the error:

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

So because I was dealing with a property I tried this:

Public Delegate Property Test() As String 

But this throws a compiler error.

So the question is, how do I make a delegate for a property?


See this link:

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

like image 338
Matt Mitchell Avatar asked Apr 07 '09 04:04

Matt Mitchell


2 Answers

Re the problem using AddressOf - if you know the prop-name at compile time, you can (in C#, at least) use an anon-method / lambda:

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

I'm not a VB expert, but reflector claims this is the same as:

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

Does that work?


Original answer:

You create delegates for properties with Delegate.CreateDelegate; this can be open for any instance of the type, of fixed for a single instance - and can be for getter or setter; I'll give an example in 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());     } } 
like image 150
Marc Gravell Avatar answered Nov 10 '22 20:11

Marc Gravell


I just create an helper with pretty good performance : http://thibaud60.blogspot.com/2010/10/fast-property-accessor-without-dynamic.html It don't use IL / Emit approach and it is very fast !

Edit by oscilatingcretin 2015/10/23

The source contains some casing issues and peculiar ="" that have to be removed. Before link rot sets in, I thought I'd post a cleaned-up version of the source for easy copy pasta, as well as an example of how to use it.

Revised source

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

Use it like this:

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))); } 
like image 23
user478450 Avatar answered Nov 10 '22 20:11

user478450