2010-11-19 12 views
2

Ich verwende einen schrecklichen Hack, um einen gesperrten Datentyp im XNA-Framework zu füllen: Es gibt eine interne Methode in einer Struktur, die ich aufrufen möchte, ohne den Garbage Collector zu füttern.Aufrufen einer internen Methode für eine Struktur

Wenn ich immer die Struktur in einer Objektvariablen verpackt und verwendet MethodInfo.Invoke(), dass Anruf würde ich den Garbage Collector füttern, indem die Parameter Boxen:

private object boxedTouchCollection; 

void test() { 
    MethodInfo addTouchLocationMethod = typeof(TouchCollection).GetMethod(
    "AddTouchLocation", BindingFlags.Instance | BindingFlags.NonPublic 
); 
    addTouchLocationMethod.Invoke(
    this.boxedState, new object[] { /* parameters being boxed */ } 
); 
} 

Ich bin nicht sicher, ob Delegate.CreateDelegate() hier verwendet werden kann - Kann ich den ersten Parameter einfach in ein Objekt umwandeln und er wird an der Box-Struktur arbeiten? Oder kann ich meine Struktur unboxed speichern und den ersten Parameter als ref TouchCollection deklarieren?

delegate void AddTouchLocationDelegate(
    ref TouchCollection collection, 
    int id, 
    // ...more parameters... 
); 

private TouchCollection touchCollection; 

void test() { 
    Delegate.CreateDelegate(
    typeof(AddTouchLocationDelegate), 
    typeof(ref TouchCollection), // doesn't compile 
    addTouchLocationMethod 
); 
} 

Gibt es eine Möglichkeit, ich Delegate.CreateDelegate() Arbeit machen? Oder muss ich auf die dynamische IL-Generierung zurückgreifen?

Antwort

3

Hier ist eine Möglichkeit.

Es beruht auf this overload von Delegate.CreateDelegate, die offene Instanz-Methode Delegaten erstellt. Das einzige knifflige Bit ist, dass Sie den entsprechenden Delegattyp erstellen müssen, um die Struktur als Referenz übergeben zu können.

Ich nicht denke, sollte es irgendwelche Boxen mit dieser Technik sein - entweder mit den Argumenten für die Methode oder mit der Struktur selbst.

Beispiel: (Apologies zur Vereinfachung der Beispiel-Typen)

public struct Foo 
{ 
    // Internal method to be called. Takes a value-type parameter. 
    internal void Test(int someParam) 
    { 
     Console.WriteLine(someParam); 
    } 

    // Custom delegate-type. Takes the Foo instance of interest 
    // by reference, as well as the argument to be passed on to Test. 
    public delegate void MyDelegate(ref Foo foo, int someParam); 

    // Creates type-safe delegate 
    private static MyDelegate GetTestDelegate() 
    { 
     var flags = BindingFlags.Instance | BindingFlags.NonPublic; 
     var methodInfo = typeof(Foo).GetMethod("Test", flags); 

     return (MyDelegate) Delegate.CreateDelegate 
          (typeof(MyDelegate), methodInfo);  
    } 

    static void Main() 
    { 
     Foo foo = new Foo(); 
     MyDelegate action = GetTestDelegate(); 

     // should dodge boxing 
     action(ref foo, 42); 
    } 
} 
+0

Vielen Dank! Ich habe diese Überladung noch nicht versucht. Funktioniert super, null Müll. – Cygon

1

Hier ist eine andere Lösung Linq Expression Baum benutzt, die ich in der Zwischenzeit gefunden:

private delegate void AddTouchLocationDelegate(
    ref TouchCollection touchCollection, 
    int id, 
    TouchLocationState state, 
    float x, 
    float y, 
    TouchLocationState prevState, 
    float prevX, 
    float prevY 
); 

private static AddTouchLocationDelegate createAddTouchLocationDelegate() { 
    MethodInfo addTouchLocationMethod = typeof(TouchCollection).GetMethod(
    "AddTouchLocation", BindingFlags.Instance | BindingFlags.NonPublic 
); 
    Type byrefTouchCollection = typeof(TouchCollection).MakeByRefType(); 

    ParameterExpression instance = Expression.Parameter(byrefTouchCollection, "instance"); 
    ParameterExpression idValue = Expression.Parameter(typeof(int), "id"); 
    ParameterExpression stateValue = Expression.Parameter(
    typeof(TouchLocationState), "state" 
); 
    ParameterExpression xValue = Expression.Parameter(typeof(float), "x"); 
    ParameterExpression yValue = Expression.Parameter(typeof(float), "y"); 
    ParameterExpression prevStateValue = Expression.Parameter(
    typeof(TouchLocationState), "prevState" 
); 
    ParameterExpression prevXValue = Expression.Parameter(typeof(float), "prevX"); 
    ParameterExpression prevYValue = Expression.Parameter(typeof(float), "prevY"); 

    Expression<AddTouchLocationDelegate> expression = 
    Expression.Lambda<AddTouchLocationDelegate>(
     Expression.Call(
     instance, addTouchLocationMethod, 
     idValue, stateValue, xValue, yValue, prevStateValue, prevXValue, prevYValue 
    ), 
     instance, 
     idValue, stateValue, xValue, yValue, prevStateValue, prevXValue, prevYValue 
    ); 

    return expression.Compile(); 
} 

Die Verwendung ist einfach:

var d = createAddTouchLocationDelegate(); 
d(
    ref this.touches, 
    1, TouchLocationState.Pressed, 10, 10, TouchLocationState.Released, 0, 0 
); 
Verwandte Themen