2010-11-19 6 views
2

J'ai recours à un hack horrible pour remplir un type de données verrouillé dans le framework XNA: il existe une méthode interne dans une structure que je souhaite appeler sans alimenter le garbage collector.Appeler une méthode interne sur une structure

Si je continue à ladite structure en boîte dans une variable d'objet et utiliser MethodInfo.Invoke(), cet appel alimenterait lui-même le garbage collector par la boxe les paramètres:

private object boxedTouchCollection; 

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

Je ne suis pas sûr que Delegate.CreateDelegate() peut être utilisé ici - puis-je simplement transformer le premier paramètre en un objet et cela fonctionnera sur la structure en boîte? Ou puis-je stocker ma structure unboxed et déclarer le premier paramètre comme ref TouchCollection?

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 
); 
} 

Est-il possible que je peux faire Delegate.CreateDelegate() travail? Ou devrais-je recourir à la génération dynamique d'IL?

Répondre

3

Voici un moyen.

Il repose sur this overload de Delegate.CreateDelegate, qui crée des délégués de méthode d'instance ouverts. Le seul petit problème est que vous devrez donc créer le type de délégué approprié pour pouvoir passer la structure par référence.

Je ne dois pas pense il devrait y avoir n'importe quelle boxe avec cette technique - soit avec les arguments à la méthode, ou avec la structure elle-même.

Exemple: (Toutes mes excuses pour simplifier les exemples types)

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

Merci beaucoup! Je n'ai pas encore essayé cette surcharge. Fonctionne très bien, zéro déchet. – Cygon

1

Voici une autre solution en utilisant des arbres d'expression Linq que je trouve dans l'intervalle:

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(); 
} 

L'utilisation est simple:

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