在结构上调用内部方法

时间:2010-11-19 16:41:00

标签: c# reflection xna touch cil

我正在使用一个可怕的黑客来填充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生成?

2 个答案:

答案 0 :(得分:3)

这是一种方式。

它依赖于Delegate.CreateDelegate 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); } } ,它创建了开放的实例方法委托。唯一棘手的一点是,所以你必须创建适当的委托类型才能通过引用传递结构。

我不认为应该有这种技术的装箱 - 要么是方法的参数,要么是结构本身。

示例:(用于简化示例类型的道歉)

{{1}}

答案 1 :(得分:1)

这是我在此期间使用Linq Expression Trees的另一个解决方案:

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