Laden...

Reflection: MethodInfo zu performantem Delegate umwandeln

Letzter Beitrag vor 13 Jahren 3 Posts 1.267 Views
Reflection: MethodInfo zu performantem Delegate umwandeln

Moin,

ich hab mir mal die ganzen Post zu Reflection angesehen, darunter auch ein Blogpost, der zeigen sollte, wie man aus einem Objekt MethodInfo einen delegatentypen erhalten kann, wenn der Typ nicht bekannt ist. Die Ansätze in allen Ehren, aber meine Tests mit diesem Quellcode

using System;
using System.Reflection;
using System.Text;
using System.Diagnostics;

public class Test
{
    public delegate W Func<T, V, W>(T arg, V val);
    public delegate int IndexOFDelegate(char c);


    static void Main()
    {

        MethodInfo indexOf = typeof(string).GetMethod("IndexOf", new Type[] { typeof(char) });
        IndexOFDelegate indexOfDelegate = (IndexOFDelegate)Delegate.CreateDelegate(typeof(IndexOFDelegate), "Hallo" ,typeof(string).GetMethod("IndexOf", new Type[] { typeof(char) }));

        MethodInfo getByteCount = typeof(Encoding).GetMethod("GetByteCount", new Type[] { typeof(string) });

        Func<string, object, object> indexOfFunc = MagicMethod<string>(indexOf);

        Func<Encoding, object, object> getByteCountFunc = MagicMethod<Encoding>(getByteCount);

        Stopwatch sw = new Stopwatch();
        sw.Start();
            indexOf.Invoke("Hello", new object[] { 'e' });
        sw.Stop();

        Console.WriteLine("Invoked MethodInfo " + sw.Elapsed.TotalMilliseconds.ToString());
        sw.Reset();
        sw.Start();
            indexOfFunc("Hello", 'e');
        sw.Stop();

        Console.WriteLine("Created Delegate " + sw.Elapsed.TotalMilliseconds.ToString());

        sw.Reset();
        sw.Start();
            indexOfDelegate('e');
        sw.Stop();

        Console.WriteLine("Declared Delegate " + sw.Elapsed.TotalMilliseconds.ToString());
        Console.ReadKey();

    }

    static Func<T, object, object> MagicMethod<T>(MethodInfo method) where T : class
    {

        // First fetch the generic form

        MethodInfo genericHelper = typeof(Test).GetMethod("MagicMethodHelper",

            BindingFlags.Static | BindingFlags.NonPublic);

        // Now supply the type arguments

        MethodInfo constructedHelper = genericHelper.MakeGenericMethod

            (typeof(T), method.GetParameters()[0].ParameterType, method.ReturnType);

        // Now call it. The null argument is because it's a static method.

        object ret = constructedHelper.Invoke(null, new object[] { method });

        // Cast the result to the right kind of delegate and return it

        return (Func<T, object, object>)ret;

    }

    static Func<TTarget, object, object> MagicMethodHelper<TTarget, TParam, TReturn>(MethodInfo method)

        where TTarget : class
    {

        // Convert the slow MethodInfo into a fast, strongly typed, open delegate

        Func<TTarget, TParam, TReturn> func = (Func<TTarget, TParam, TReturn>)Delegate.CreateDelegate

            (typeof(Func<TTarget, TParam, TReturn>), method);

        // Now create a more weakly typed delegate which will call the strongly typed one

        Func<TTarget, object, object> ret = delegate(TTarget target, object param) { return func(target, (TParam)param); };

        return ret;

    }

}

haben angehängtes Ergebnis auf einer 32b XP-Maschine unter 2.0 geliefert, wobei ich mich dann doch fragen muss, was sich die ganzen Programmierer über die schlechte Performance von MethodInfo.Invoke() beschweren.


Gibt es denn noch irgendeine andere Möglichkeit unter 2.0 zur Laufzeit einen delegaten aus einem MethodInfo-Objekt zu erzeugen, dass an die Geschwindigkeit eines zur Buildzeit definierten delegaten herankommt?

EDIT: Eventuell indem man einen Methodenpointer in einen delegaten castet?

Hallo Pria,

Gibt es denn noch irgendeine andere Möglichkeit unter 2.0 zur Laufzeit einen delegaten aus einem MethodInfo-Objekt zu erzeugen, dass an die Geschwindigkeit eines zur Buildzeit definierten delegaten herankommt?

Klar, schau dir einfach die Überladungen von Delegate.CreateDelegate an.

BTW: deine Bezeichnungen im Test passen nicht ganz. Unter "Declared Delegate" versteht sich normal einer der dirket im Code deklariert wird.

Im Release-Build ohne Debugger hast du auch gemessen und das JITes jeweils berückichtigt? Aber der Aufwand für MagicMethod ist ohnehin gar nicht nötig.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

Da tut sich zum release nicht viel zwischen dem Delegaten und dem Invoke. Das Problem bei delegate.CreateDelegate ist der verlangte Typ, der wiederum ein Delegate mit exakt definiertem Typ sein muss, der eben nicht genau bekannt ist.