#c# #reflection #xna #touch #cil
#c# #отражение #xna #коснитесь #cil
Вопрос:
Я прибегаю к ужасному взлому, чтобы заполнить закрытый тип данных в XNA framework: в структуре есть внутренний метод, который я хочу вызвать, не загружая сборщик мусора.
Если я сохраню указанную структуру в ячейке объектной переменной и использую 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?
Ответ №1:
Вот один из способов.
Он полагается на эту перегрузку 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. Большое вам спасибо! Я еще не пробовал эту перегрузку. Отлично работает, никакого мусора.
Ответ №2:
Вот еще одно решение, использующее деревья выражений 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
);