The "Uncurrying" instance method in .NET.

Can you create an instance method delegate without specifying an instance at creation time? In other words, is it possible to create a “static” delegate that accepts the instance on which the method should be called as the first parameter?

For example, how can I build the next delegate using reflection?

Func<int, string> = i=>i.ToString(); 

I know that I can use methodInfo.Invoke, but it is slower and does not check the correctness of the text until it is called.

When you have a MethodInfo specific static method, you can build a delegate using Delegate.CreateDelegate(delegateType, methodInfo) , and all the parameters of the static method remain free.

As John Skeet pointed out, you can simply apply the same to make an instance method public delegate if the method is not virtual in the reference type. The decision about which method to call using the virtual method is difficult, so it’s not so trivial, and the types of values ​​look like they don’t work at all.

For value types, CreateDelegate exhibits a really strange behavior:

 var func37 = (Func<CultureInfo,string>)(37.ToString); var toStringMethod = typeof(int).GetMethod("ToString", BindingFlags.Instance | BindingFlags.Public, null, new Type[] {typeof(CultureInfo) }, null); var func42 = (Func<CultureInfo,string>)Delegate.CreateDelegate(typeof(Func<CultureInfo,string>), 42, toStringMethod,true); Console.WriteLine( object.ReferenceEquals(func37.Method,func42.Method)); //true Console.WriteLine(func37.Target);//37 Console.WriteLine(func42.Target);//42 Console.WriteLine(func37(CultureInfo.InvariantCulture));//37 Console.WriteLine(func42(CultureInfo.InvariantCulture));//-201040128... WTF? 

Call CreateDelegate with null because the target throws a bind exception if the instance method belonged to a value type (this works for reference types).

The next few years: an incorrectly bound target that caused func42(CultureInfo.InvariantCulture); to return "-201040128" instead of "42" , in my example there was a memory corruption that could allow remote code execution ( cve-2010-1898 ); this was fixed in 2010 in security update ms10-060 . The current frames correctly print 42! This does not facilitate the answer to this question, but explains the particularly strange behavior in this example.

+5
source share
4 answers

In fact, you have chosen a particularly complex example for two reasons:

  • ToString () is a virtual method inherited from object but overridden in Int32 .
  • int is a value type, and there are strange rules with Delegate.CreateDelegate() when it comes to value types and instance methods - basically the first effective parameter becomes ref int , not int

However, here is an example for String.ToUpper that does not have any of these problems:

 using System; using System.Reflection; class Test { static void Main() { MethodInfo method = typeof(string).GetMethod ("ToUpper", BindingFlags.Instance | BindingFlags.Public, null, new Type[]{}, null); Func<string, string> func = (Func<string, string>) Delegate.CreateDelegate(typeof(Func<string, string>), null, method); string x = func("hello"); Console.WriteLine(x); } } 

If you feel good, great ... if you really want an int.ToString , I will have to try a little harder :)

Here's an example of a value type using the new delegate type, which takes its first parameter by reference:

 using System; using System.Reflection; public struct Foo { readonly string value; public Foo(string value) { this.value = value; } public string DemoMethod() { return value; } } class Test { delegate TResult RefFunc<TArg, TResult>(ref TArg arg); static void Main() { MethodInfo method = typeof(Foo).GetMethod ("DemoMethod", BindingFlags.Instance | BindingFlags.Public, null, new Type[]{}, null); RefFunc<Foo, string> func = (RefFunc<Foo, string>) Delegate.CreateDelegate(typeof(RefFunc<Foo, string>), null, method); Foo y = new Foo("hello"); string x = func(ref y); Console.WriteLine(x); } } 
+9
source

I'm not sure, but maybe Open delegates can help you.

Update: follow this link if the first one does not work.

+3
source

You can use Lambdas to get a "somewhat" compiled static wrapper for your instance method.

The example below is not entirely fast, but it should be significantly faster than any regular dynamic call.

Output

 100000 iterations took 4 ms 1000000 iterations took 18 ms 10000000 iterations took 184 ms 

Code

 class Program { public sealed class Test { public String Data { get; set; } public override string ToString() { return Data; } } static void Main(string[] args) { TestRun(100000); TestRun(1000000); TestRun(10000000); } private static void TestRun(int iterations) { var toString = typeof(Test).GetMethod("ToString", BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null); var call = GetCall<Test, String>(toString); var tests = (from i in Enumerable.Range(1, iterations) select new Test { Data = "..." + i }).ToList(); var sw = Stopwatch.StartNew(); tests.ForEach(i => call(i)); sw.Stop(); Console.WriteLine("{0} iterations took {1} ms", iterations, sw.ElapsedMilliseconds); } private static Func<T, M> GetCall<T, M>(MethodInfo methodInfo) { var input = Expression.Parameter(typeof(T), "input"); MethodCallExpression member = Expression.Call(input, methodInfo); var lambda = Expression.Lambda<Func<T, M>>(member, input); return lambda.Compile(); } } 
+2
source

The goog-way may possibly use the "dynamic" type in .NET 4.0. However, the delegate needs an instance (for non-static methods). The problems are harder than lokks for the first time due to polymorphism, etc.

0
source

All Articles