How do I create a delegate for a .NET property?
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());
}
}
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)));
}
Here is a C#/.NET 2.0 version of 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);
}
}
Again, 'Delegate.CreateDelegate' is what is fundamental to this example.
This is good idea
Test t = () => e.PropertyName; // C# 3.0
But take care if your are doing something like this:
List<Func<int>> funcs = new List<Func<int>>();
foreach (var e in Collection)
funcs.Add(new Func<int>(() => e.Property));
Calling this:
foreach(var f in funcs)
f();
Will always return value of property of the last object in Collection
In this case you should call method:
foreach (var e in Collection)
funcs.Add(new Func<int>(e.GetPropValue));