For properties there are GetGetMethod
and GetSetMethod
so that I can do:
Getter = (Func<S, T>)Delegate.CreateDelegate(typeof(Func<S, T>),
propertyInfo.GetGetMethod());
and
Setter = (Action<S, T>)Delegate.CreateDelegate(typeof(Action<S, T>),
propertyInfo.GetSetMethod());
But how do I go about FieldInfo
s?
I am not looking for delegates to GetValue
and SetValue
(which means I will be invoking reflection each time)
Getter = s => (T)fieldInfo.GetValue(s);
Setter = (s, t) => (T)fieldInfo.SetValue(s, t);
but if there is a CreateDelegate
approach here? I mean since assignments return a value, can I treat assignments like a method? If so is there a MethodInfo
handle for it? In other words how do I pass the right MethodInfo
of setting and getting a value from a member field to CreateDelegate
method so that I get a delegate back with which I can read and write to fields directly?
Getter = (Func<S, T>)Delegate.CreateDelegate(typeof(Func<S, T>), fieldInfo.??);
Setter = (Action<S, T>)Delegate.CreateDelegate(typeof(Action<S, T>), fieldInfo.??);
I can build expression and compile it, but I am looking for something simpler. In the end I don't mind going the expression route if there is no answer for the asked question, as shown below:
var instExp = Expression.Parameter(typeof(S));
var fieldExp = Expression.Field(instExp, fieldInfo);
Getter = Expression.Lambda<Func<S, T>>(fieldExp, instExp).Compile();
if (!fieldInfo.IsInitOnly)
{
var valueExp = Expression.Parameter(typeof(T));
Setter = Expression.Lambda<Action<S, T>>(Expression.Assign(fieldExp, valueExp), instExp, valueExp).Compile();
}
Or am I after the nonexistent (since I have nowhere seen something like that yet) ?
As Peter Ritchie suggested, you can compile your own code at runtime. The method will be compiled as soon as you invoke the delegate for the first time. So the first call will be slow, but any subsequent call will be as fast as you can get in .NET without unmanaged pointers/unions. Except for the first call, the delegate is around 500 times faster than FieldInfo directly.
class DemoProgram
{
class Target
{
private int value;
}
static void Main(string[] args)
{
FieldInfo valueField = typeof(Target).GetFields(BindingFlags.NonPublic| BindingFlags.Instance).First();
var getValue = CreateGetter<Target, int>(valueField);
var setValue = CreateSetter<Target, int>(valueField);
Target target = new Target();
setValue(target, 42);
Console.WriteLine(getValue(target));
}
static Func<S, T> CreateGetter<S, T>(FieldInfo field)
{
string methodName = field.ReflectedType.FullName + ".get_" + field.Name;
DynamicMethod setterMethod = new DynamicMethod(methodName, typeof(T), new Type[1] { typeof(S) }, true);
ILGenerator gen = setterMethod.GetILGenerator();
if (field.IsStatic)
{
gen.Emit(OpCodes.Ldsfld, field);
}
else
{
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldfld, field);
}
gen.Emit(OpCodes.Ret);
return (Func<S, T>)setterMethod.CreateDelegate(typeof(Func<S, T>));
}
static Action<S, T> CreateSetter<S,T>(FieldInfo field)
{
string methodName = field.ReflectedType.FullName+".set_"+field.Name;
DynamicMethod setterMethod = new DynamicMethod(methodName, null, new Type[2]{typeof(S),typeof(T)},true);
ILGenerator gen = setterMethod.GetILGenerator();
if (field.IsStatic)
{
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Stsfld, field);
}
else
{
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Stfld, field);
}
gen.Emit(OpCodes.Ret);
return (Action<S, T>)setterMethod.CreateDelegate(typeof(Action<S, T>));
}
}
Keep in mind that structs are passed by value. That means an Action<S, T>
can not be used to change members of a struct if it is passed by value as the first argument.