I am working on a caching solution to get all property fields of a class, create getter and setter delegates out of them, and store them.
This is how I do it at the moment:
// entity is a class with a single public property called 'Name'
var entity = new Entity("Cat");
var nameProp = typeof(Entity)
.GetProperties()
.Single(x => x.Name == nameof(Entity.Name));
// getting getter and setter methods
var namePropGetter = nameProp.GetGetMethod();
var namePropSetter = nameProp.GetSetMethod();
// creating delegates for getter and setter methods
var namePropGetterDelegate = (Func<Entity, string>)Delegate.CreateDelegate(typeof(Func<Entity, string>), namePropGetter);
var namePropSetterDelegate = (Action<Entity, string>)Delegate.CreateDelegate(typeof(Action<Entity, string>), namePropSetter);
All getters are to be stored in one Dictionary and all setters are to be stored in another. This would let me quickly get or set values of an object rather than using a traditional PropertyField.GetValue()
or PropertyField.SetValue()
.
The only problem is the storage of getter and setter delegates.
I tried storing all getters into Func<TargetClass, object>
and setters into Action<TargetClass, object>
like so:
var getterDelegate = (Func<Entity, object>)Delegate.CreateDelegate(typeof(Func<Entity, object>), namePropGetter);
var setterDelegate = (Action<Entity, object>)Delegate.CreateDelegate(typeof(Action<Entity, object>), namePropSetter);
But it would give me the following error:
Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.
Storing everything inside of Delegate
would force me to use DynamicInvoke()
, which defeats the purpose of caching everything in the first place because it's slow.
The error happens because you pass an incorrect type to Delegate.CreateDelegate
. For instance, for the getter of an Int32
property you should pass Func<Entity, Int32>
and not Func<Entity, object>
. This can be achieved with typeof(Func<,>).MakeGenericType(Entity, propType)
.
If you want to have a dictionary of delegates per property name, you should use Dictionary<string, Delegate>
, then cast Delegate
to a specific delegate type before invoking it:
var gettersDictionary = new Dictionary<string, Delegate>();
var settersDictionary = new Dictionary<string, Delegate>();
foreach (var prop in typeof(Entity).GetProperties())
{
var getterDelegate = Delegate.CreateDelegate(
typeof(Func<,>).MakeGenericType(typeof(Entity), prop.PropertyType),
prop.GetGetMethod());
gettersDictionary.Add(prop.Name, getterDelegate);
var setterDelegate = Delegate.CreateDelegate(
typeof(Action<,>).MakeGenericType(typeof(Entity), prop.PropertyType),
prop.GetSetMethod());
settersDictionary.Add(prop.Name, setterDelegate);
}
T GetPropValue<T>(Entity entity, string name)
{
var getter = (Func<Entity, T>) gettersDictionary[name];
return getter(entity);
}
void SetPropValue<T>(Entity entity, string name, T value)
{
var setter = (Action<Entity, T>) settersDictionary[name];
setter(entity, value);
}