2010-11-19 86 views
2

爲了在XNA框架中填充鎖定的數據類型,我採取了一種可怕的方法:在結構中有一個內部方法,我希望在不餵食垃圾回收器的情況下調用該方法。在結構上調用內部方法

如果我繼續說結構對象變量盒裝和使用MethodInfo.Invoke(),該呼叫本身將通過拳擊參數喂垃圾收集器:

private object boxedTouchCollection; 

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

我不知道是否Delegate.CreateDelegate()這裏可以使用 - 我可以只將第一個參數變成一個對象,它可以在盒裝結構上工作嗎?或者我可以存儲我的結構拆箱並聲明第一個參數爲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 
); 
} 

有沒有一種辦法可以讓Delegate.CreateDelegate()工作? 或者我將不得不訴諸動態IL一代?

回答

3

這是一種方法。

它依賴Delegate.CreateDelegatethis overload,它創建開放的實例方法委託。唯一棘手的是,所以你必須創建適當的委託類型,以便能夠通過引用來傳遞結構。

我不會認爲應該有任何拳擊與這種技術 - 無論是與方法的參數,或結構本身。

實施例:(道歉簡化示例類型)

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

非常感謝!我沒有嘗試過載。偉大的工作,零垃圾。 – Cygon 2010-11-19 19:38:06

1

下面是使用我在此期間發現LINQ表達式樹的另一個解決方案:

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

用法是直接的:

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