Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Open instance delegates to value types

Tags:

c#

clr

delegates

Delegate.CreateDelegate fails when I try to create open instance delegates to value type methods that implement interfaces. Because the instance method is on a value type, the first argument must be a ref, so let's define a general delegate type for this:

delegate T2 VF<T0, T1, T2>(ref T0 arg0, T1 arg1);

Now here's a program that succeeds in creating an open instance delegate to int.ToString(string), but fails to create an open instance delegate to int.Equals(int):

class Program
{
    static void Main(string[] args)
    {
        var tos = typeof(int).GetMethod("ToString", new[] { typeof(string) });
        var tosopen = Delegate.CreateDelegate(
                          typeof(VF<int, string, string>), null, tos);
        // success!

        var eq = typeof(int).GetMethod("Equals", new[] { typeof(int) });
        var eqopen = Delegate.CreateDelegate(
                         typeof(VF<int, int, bool>), null, eq);
        // previous line fails with "Error binding to target method"
    }
}

This seems to be due to the fact that int.Equals(int) implements IEquatable<int>. Note that the following does work:

var x = typeof(IEquatable<int>).GetMethod("Equals", new Type[] { typeof(int) });
var xopen = Delegate.CreateDelegate(
                typeof(Func<IEquatable<int>, int, bool>), null, x);
xopen(1,1); // returns true

But this isn't exactly what I want, since this would seem to box any integers passed as the first argument. I also don't want to compare any IEquatable<int>, I want to specifically call the method to compare two ints.

Any thoughts on what's wrong here?

A Microsoft Connect bug was mentioned here that seems directly related, but that link no longer works for me, and I can't find that bug by searching.

Edit: note that the same problem occurs when trying to create open instance delegates to overridden methods, so it's not just interface methods.

like image 863
naasking Avatar asked Nov 13 '22 19:11

naasking


1 Answers

This ran as part of a time test, hope it helps.

    public delegate Boolean RefFunc<T>(ref T arg1, Object arg2);
    static void Main(string[] args)
    {
        double loops = 1e6;
        Random random = new Random();
        System.Reflection.MethodInfo m;
        Stopwatch stopwatch = new Stopwatch();
        Type type = typeof(double);

        double tmp;
        stopwatch.Reset();
        stopwatch.Start();
        var deligates = new Dictionary<Type, RefFunc<double>>();
        RefFunc<double> d;
        for (int ii = 0; ii < loops; ii++)
        {
            if (!deligates.TryGetValue(type, out d))
            {
                m = type.GetMethod("Equals", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(Object) }, null);
                d = (RefFunc<double>)Delegate.CreateDelegate(typeof(RefFunc<double>), null, m);
                deligates[typeof(double)] = d;
            }
            tmp = Convert.ToDouble(random.Next(0, 100));
            d(ref tmp, Convert.ToDouble(random.Next(0, 100)));
        }
        stopwatch.Stop();
        Console.WriteLine("Delegate " + stopwatch.Elapsed.ToString());
        Console.WriteLine("Delegate " + (stopwatch.Elapsed.Ticks / loops).ToString());

        Console.WriteLine("");
    }

Edit: Just noticed the original post's date, but it could still save others a lot of time.

like image 163
Dante Parker Avatar answered Nov 15 '22 09:11

Dante Parker