调用结构体的内部方法

发布于 2024-10-03 21:44:01 字数 1049 浏览 7 评论 0原文

我正在采取一种可怕的黑客手段来填充 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 生成?

I am resorting to a horrible hack in order to fill a locked-down data type in the XNA framework: there's an internal method in a structure that I wish to call without feeding the garbage collector.

If I keep said structure boxed in an object variable and use MethodInfo.Invoke(), that call would itself feed the garbage collector by boxing the parameters:

private object boxedTouchCollection;

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

I'm not sure whether Delegate.CreateDelegate() can be used here - can I just turn the first parameter into an object and it will work on the boxed structure? Or can I store my structure unboxed and declare the first parameter as 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
  );
}

Is there a way I can make Delegate.CreateDelegate() work?
Or will I have to resort to dynamic IL generation?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

傲世九天 2024-10-10 21:44:01

这是一种方法。

它依赖于 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);
    }
}

Here's one way.

It relies on this overload of Delegate.CreateDelegate, which creates open instance-method delegates. The only tricky bit is that so you'll have to create the appropriate delegate-type to be able to pass the struct by reference.

I don't think there should be any boxing with this technique - either with the arguments to the method, or with the struct itself.

Example: (Apologies for simplifying the example-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);
    }
}
是你 2024-10-10 21:44:01

这是我同时发现的另一个使用 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
);

Here's another solution using Linq Expression Trees that I found in the meantime:

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

Usage is straightforward:

var d = createAddTouchLocationDelegate();
d(
  ref this.touches,
  1, TouchLocationState.Pressed, 10, 10, TouchLocationState.Released, 0, 0
);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文