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) ?
Using the new “ref return” feature in C# 7.0 can make the process of creating and using runtime dynamically-generated get/set accessors much simpler and syntactically transparent. Instead of having to use DynamicMethod to emit separate getter and setter functions for accessing the field, you can now have a single method that returns a managed pointer-type reference to the field, essentially a single accessor that (in turn) enables convenient, ad-hoc get a̲n̲d̲ set access. Below, I provide a helper utility function which simplifies generating a ByRef getter function for any arbitrary (i.e. private) instance field in any class.
➜ For “just the code,” skip to note below.
As a running example, let's say we want to access a private instance field
m_iPrivate
, anint
defined in the classOfInterestClass
:Next let's assume we have a static field “reference-getter” function that takes a
OfInterestClass
instance and returns the desired field value by reference using the new C# 7 “ref return” capability (below, I'll provide code to generate such functions at runtime, via DynamicMethod):Such a function (“ref-getter,” let's say) is all we need in order to to have full read/write access to the private field. In the following examples, note especially the setter-invoking operation—and the demonstrations of using the (i.e.)
++
and+=
operators—since applying those operators directly to a method call may look a little unusual if you're not up-to-speed on C#7.As is the point, every operation shown in these examples manipulates
m_iPrivate
in situ (i.e., directly within its containing instanceoic
) such that any and all changes are publicly visible there immediately. It's important to realize that this means thatprv
, despite beingint
-typed and locally declared, does not behave like your typical “local” variable. This is especially significant for concurrent code; not only are changes visible b̲e̲f̲o̲r̲e̲MyFunction
has exited, but now with C# 7, callers have the ability to retain a ref return managed pointer (as a ref local) and thus continue modifying the target for an arbitrarily long time a̲f̲t̲e̲r̲wards.Of course a main and obvious advantage of using a managed pointer here (and anywhere in general) is that it always remains valid, even as
oic
—itself a reference-type instance allocated in the GC heap—may be moved around during garbage collection. This is a gigantic difference versus native pointers.As sketched above, the ref-getter is a
static
extension method that can be declared and/or used from anywhere. But if you're able to create your own class that's derived fromOfInterestClass
(that is, ifOfInterestClass
isn't sealed), you can make this even nicer. In a derived class, you can expose C# syntax for using the base class's private field as if it were a public field of your derived class. To do this, just add a C# read-only ref return property to your class which binds the static ref-getter method to the current instancethis
:Here, the property is made
public
so anybody can access the field (via a reference to our derived class). We've essentially publicly published the private field from the base class. Now, in the derived class (or elsewhere, as appropriate) you can do any or all of the following:As you can see, because the property, like the earlier method, also has a by reference return value, it behaves almost exactly like a field does.
So now for the details. How do you create the static ref-getter function that I showed above? Using
DynamicMethod
, this should be trivial. For example, here is the IL code for a traditional (by-value) static getter function:And here is the IL code that we want instead (ref-return):
The only difference from the by-value getter is that we are using the
ldflda
(load field address) opcode instead ofldfld
(load field). So if you're well-practiced withDynamicMethod
it should be no problem, right?If you try to call the
DynamicMethod
constructor specifying aByRef
type as the return value......the function throws
NotSupportedException
with the following message:Apparently, this function did not get the memo on C#7 and ref-return. Fortunately, I found a simple workaround that gets it working. If you pass a non-ref type into the constructor as a temporary "dummy," but then immediately afterwards use reflection on the newly-created
DynamicMethod
instance to change itsm_returnType
private field to be the ByRef-type type (sic.) that you actually want, then everything seems to work just fine.To speed things up, I'll cut to the completed generic method which automates the whole process of by creating/returning a static ref-getter function for the private instance field of type
U
, having the supplied name, and defined in classT
.If you just want the complete working code, copy from below this point to the end
First we have to define a delegate that represents the ref-getter, since a
Func<T,TResult>
delegate with ByRef usage can't be declared. Fortunately, the olderdelegate
syntax does work for doing so (phew!).Place the delegate, along with the following static function in a centralized utility class where both can be accessed throughout your project. Here's the final ref-getter creation function which can be used to create a static ref-getter for the so-named instance field in any class.
Returning now to outset of this article, we can easily provide the
__refget_m_iPrivate
function that got everything started. Instead of a static function written directly in C#, we will use the static ref-getter creation function to create the function body at runtime and store it in a static delegate-typed field (with the same signature). The syntax for calling it in the instance property (as shown above, and repeated below) or elsewhere is the same as if the compiler had been able to write the function.Finally, to cache the dynamically-created ref-getter delegate, place the following line in any
static
class of your choice. ReplaceOfInterestClass
with the type of the base class,int
with the field type of the private field, and change the string argument to match the name of the private field. If you aren't able to create your own class derived fromOfInterestClass
(or don't want to), you're done; just make this fieldpublic
and you can call it like a function, passing anyOfInterestClass
instance to get a reference which lets you read, write, or monitor itsint
-valuedprivate
field "m_iPrivate
."Optionally, if you want to publish the hidden field with a cleaner or more natural syntax, you can define a (non-static) proxy class of your own which either contains an instance of—or perhaps even better (if possible), derives from—the field hiding class
OfInterestClass.
Instead of deploying the line of code previously shown globally in astatic
class, place it in your proxy class instead, and then also add the following line: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.
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.Here is an another option for creating a delegate when you are working with objects (don't know specific type of a field). Though it is slower if field is a structure (because of boxing).
I'm not aware if you would use
Expression
, then why avoiding reflection? Most operations ofExpression
rely on reflection.GetValue
andSetValue
themself are theget method
andset method
, for the fields, but they are not for any specific field.Fields are not like properties, they are fields, and there's no reason to generate get/set methods for each one. However, the type may vary with different field, and thus
GetValue
andSetValue
are defined theparameter/return value
asobject
for variance.GetValue
is even a abstract method, that is, for every class(still reflection) overriding it, must be within the identical signature.If you don't type them, then the following code should do:
but if you want, there's a constrained way:
For the reason that the
Getter
still beFunc<S, object>
, you might want to have a look of:Covariance and Contravariance in C#, Part Three: Method Group Conversion Variance on Mr. Lippert's blog.
Field access isn't performed via a method (like getters and setters)--it's performed with an IL instruction--so there's nothing you can assign to a delegate. you'll have to use the expression route to create a "block" of code (effectively IL) that can be assigned to a delegate.
No there is no easy way to create a delegate to get/set a field.
You will have to make your own code to provide that functionallity. I would suggest two functions in a shared library to provide this.
Using your code (in this example I only show the creation of the get-delegate):
This makes it easy to create a get-delegate from a FieldInfo (assuming the field is of type int):
Or if we change your code a little bit:
This makes it even more easy:
It is also possible to create these delegates using IL, but that code would be more complex and does not have much more performance, if any.