Здравствуйте, alxrie, Вы писали:
A>Здравствуйте, maloi_alex,
A>Речь несколько об ином. После вызова A>k будет иметь значение 1. A>А надо, чтобы вызов через рефлексию дал тот же результат, что и "прямой" вызов метода.
Можно создать Expression, скомплировать его в delegate и выполнить:
var mi = typeof(Test).GetMethod("Proc");
var obj = Expression.Parameter(typeof(Test));
var p1 = Expression.Parameter(typeof(int));
var b = Expression.Block(
typeof(int), // тип результата этого блока
Expression.Call(obj, mi, p1, p1), // вызываем метод, передавая один и тот же параметр в оба аргумента
p1); // результат выполнения блокаvar l = Expression.Lambda<Func<Test, int, int>>(
b,
obj, p1);
var d = l.Compile();
var k = 0;
var k2 = d(new Test(), k);
k2 в результате будет равна 2, как вы и хотите.
А почему в ваш метод передается по ссылке одна и та же переменная? В этом какой-то особенный смысл? Ведь нет никакой гарантии, в каком порядке произойдет изменение значений параметров внутри вызываемого метода.
Рефлексия на самом деле передает все ref-аргументы туда и обратно. Просто она передает в метод ссылки на разные элементы массива аргументов, поэтому они и изменяются независимо.
Еще вариант: сгенерить метод-"обертку" — lightweight dynamic метод — с помощью System.Reflection.Emit.DynamicMethod с такой же сигнатурой как у вызываемого метода и выполнить его. Тогда вы получите нужный эффект, независимо от того, одну переменную передаете или разные. Недостаток этого метода в том, что нужно объявлять делегат соответствующий вызываемому методу.
Re: Как корректно передать параметр по ссылке при рефлексии в C#?
Если метод или конструктор, представленный этим экземпляром, принимает параметр ref (ByRef в Visual Basic), для вызова метода или конструктора с использованием этой функции не требуются никакие специальные атрибуты. Любой объект этого массива, который не был явно инициализирован с помощью значения, будет содержать значение по умолчанию для данного типа объекта. Для элементов ссылочного типа это значение равно nullNothingnullptrссылка null (Nothing в Visual Basic). Для элементов, тип которых соответствует значению, это значение равно 0, 0,0 или false, в зависимости от заданного типа элемента.
Re[2]: Как корректно передать параметр по ссылке при рефлексии в C#?
Здравствуйте, alxrie, Вы писали:
A>Речь несколько об ином. После вызова A>А надо, чтобы вызов через рефлексию дал тот же результат, что и "прямой" вызов метода.
Интересно...
А у Tes.Proc() сигнатуру нельзя менять? Можно было бы тогда обертку сделать:
class ByRef<T>
{
public ByRef(T value) { Value = value;}
public T Value { get; private set; }
}
public void Proc(ByRef<int> i, ByRef<int> int j)
{
i.Value++; j.Value++;
}
ByRef<int> k = new ByRef<int>(0);
object[] args = new object[] { k, k };
typeof(Test).GetMethod("Proc").Invoke(new Test(), args);
Re[4]: Как корректно передать параметр по ссылке при рефлексии в C#?
Здравствуйте, maloi_alex, Вы писали:
M>А у Tes.Proc() сигнатуру нельзя менять?
Увы, нельзя... Настоящий класс уже используется, надо лишь "достучаться" до его методов через рефлексию.
Re[3]: Как корректно передать параметр по ссылке при рефлексии в C#?
Здравствуйте, sergeyt4, Вы писали:
S>Можно создать Expression, скомплировать его в delegate и выполнить:
S>
S>var mi = typeof(Test).GetMethod("Proc");
S>var obj = Expression.Parameter(typeof(Test));
S>var p1 = Expression.Parameter(typeof(int));
S>var b = Expression.Block(
S> typeof(int), // тип результата этого блока
S> Expression.Call(obj, mi, p1, p1), // вызываем метод, передавая один и тот же параметр в оба аргумента
S> p1); // результат выполнения блока
S>var l = Expression.Lambda<Func<Test, int, int>>(
S> b,
S> obj, p1);
S>var d = l.Compile();
S>var k = 0;
S>var k2 = d(new Test(), k);
S>
S>k2 в результате будет равна 2, как вы и хотите.
Я хочу не этого. Test.Proc — она ведь вообще void. Я хочу, чтобы k==2. В моем случае методы возвращают (дополнительные) результаты, модифицируя параметры-ссылки.
S>А почему в ваш метод передается по ссылке одна и та же переменная? В этом какой-то особенный смысл? Ведь нет никакой гарантии, в каком порядке произойдет изменение значений параметров внутри вызываемого метода.
Могут передаваться разные переменные, может передаваться одна и та же переменная. Рефлексия потому и используется, что априори неизвестно, кто и с какими параметрами будет вызван, это выясняется лишь "по ходу пьесы".
Реальные классы, естественно, более сложные, чем приведенный пример, задача в том, чтобы обеспечить идентичное поведение — как при прямом вызове, так и через рефлексию.
S>Рефлексия на самом деле передает все ref-аргументы туда и обратно. Просто она передает в метод ссылки на разные элементы массива аргументов, поэтому они и изменяются независимо.
Это понятно. И приведенный пример как раз это и иллюстрировал.
S>Еще вариант: сгенерить метод-"обертку" — lightweight dynamic метод — с помощью System.Reflection.Emit.DynamicMethod с такой же сигнатурой как у вызываемого метода и выполнить его. Тогда вы получите нужный эффект, независимо от того, одну переменную передаете или разные. Недостаток этого метода в том, что нужно объявлять делегат соответствующий вызываемому методу.
Спасибо! С DynamicMethod жить стало легче.
Осталось решить указанную Вами проблему с необходимостью объявления делегатов.