How to create an open delegate from an instance method of a structure?

I have a private method structure that I would like to call. Since I plan to do this in a performance-critical section, I would like to cache the delegate to perform the action. The problem is that I cannot bind to its method using Delegate.CreateDelegate. This structure is not my creation and is used in conjunction with a third-party library. The corresponding structure looks like this:

public struct A { private int SomeMethod() { //body go here } } 

And the following code will fail with the error "Binding errors with the target method."

 Delegate.CreateDelegate(typeof(Func<A,int>),typeof(A).GetMethod("SomeMethod",BindingFlags.Instance | BindingFlags.NonPublic)); 

I know that I can write an expression tree to perform an action, but it seems strange that I cannot use my usual goto method for these things with the Delegate.CreateDelegate method.

The above code works fine if A was a class. The problem arises only because A is a structure. The MSDN documentation is incorrect for this CreateDelegate overload because it works with non-stationary methods.

+3
source share
3 answers

An interesting problem. From this bug report, it looks like it could be a bug that will be fixed in a future version of .NET: http://connect.microsoft.com/VisualStudio/feedback/details/574959/cannot-create-open-instance- delegate-for-value-types-methods-which-implement-an-interface # details

EDIT: Actually, I think this error report addresses another issue, so the behavior you see cannot actually be an error.

From this error report, I realized that there is a workaround if you specify the first argument of your delegate as passed by reference. The following is a complete working example:

 public struct A { private int _Value; public int Value { get { return _Value; } set { _Value = value; } } private int SomeMethod() { return _Value; } } delegate int SomeMethodHandler(ref A instance); class Program { static void Main(string[] args) { var method = typeof(A).GetMethod("SomeMethod", BindingFlags.Instance | BindingFlags.NonPublic); SomeMethodHandler d = (SomeMethodHandler)Delegate.CreateDelegate(typeof(SomeMethodHandler), method); A instance = new A(); instance.Value = 5; Console.WriteLine(d(ref instance)); } } 

EDIT: Jon Skeet's answer here also discusses this issue.

+7
source

The first delegate parameter of an unbound instance method cannot be a value type. This is due to how value types should be handled when using 'this' as parameters. You cannot just pass them by value (as it would if you were passing the value type as the first parameter of a static method), as then the method acts on the copy, and any copy mutation does not affect the original.


UPDATE: As noted in another answer, the types of values ​​used as 'this' parameters are effectively passed by reference.

+1
source

You use this CreateDelegate overload :

Creates a delegate of the specified type to represent the specified static method.

SomeMethod not a static method.

Use overload which allows you to specify the target :

 A target = new A(); Func<int> f = (Func<int>)Delegate.CreateDelegate( typeof(Func<int>), target, typeof(A).GetMethod( "SomeMethod", BindingFlags.Instance | BindingFlags.NonPublic)); 

This means that you need to create a delegate for each instance of A You cannot reuse the same delegate for different instances.

The best solution is probably to build a Lambda expression using LINQ expression trees:

 var p = Expression.Parameter(typeof(A), "arg"); var lambda = Expression.Lambda<Func<A, int>>( Expression.Call( p, typeof(A).GetMethod( "SomeMethod", BindingFlags.Instance | BindingFlags.NonPublic)), p); Func<A, int> f = lambda.Compile(); 
0
source

All Articles