Re[2]: Как корректно передать параметр по ссылке при рефлексии в C#?
От: alxrie  
Дата: 28.02.13 05:29
Оценка: 68 (1)
Здравствуйте, Sinclair, Вы писали:

S>А что, вот так — тоже не работает?

S>
S>object k = (int)0;
S>object[] args = new object[] { k, k };
S>typeof(Test).GetMethod("Proc").Invoke(new Test(), args);
S>var res = (int)k;
S>


Так вообще не работает — res==0.
Если
res = (int)arg[0];

то res==1.
Так что боксинг не спасает.
Re[3]: Как корректно передать параметр по ссылке при рефлексии в C#?
От: sergeyt4  
Дата: 27.02.13 22:27
Оценка: 1 (1)
Здравствуйте, 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#?
От: Sinclair Россия https://github.com/evilguest/
Дата: 28.02.13 03:44
Оценка: :)
Здравствуйте, alxrie, Вы писали:

A>Например, есть класс, а у класса — метод с параметрами, передаваемыми по ссылке:

A>
public class Test()
A>{ public Test() { }
A>  public void Proc(ref int i, ref int j)
A>  { i++; j++; }
A>}

A>С этим классом будут работать через рефлексию. Как передать параметр по ссылке в таком случае?
A>Вариант
A>
int k=0;
A>object[] args = new object[] { k, k };
A>typeof(Test).GetMethod("Proc").Invoke(new Test(), args);
A>k = (int)(args[0]);

A>естественно, не годится — получится по значению/результату, а не по ссылке.
A>Как быть?
А что, вот так — тоже не работает?
object k = (int)0;
object[] args = new object[] { k, k };
typeof(Test).GetMethod("Proc").Invoke(new Test(), args);
var res = (int)k;
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Как корректно передать параметр по ссылке при рефлексии в C#?
От: alxrie  
Дата: 27.02.13 00:57
Оценка:
Например, есть класс, а у класса — метод с параметрами, передаваемыми по ссылке:
public class Test()
{ public Test() { }
  public void Proc(ref int i, ref int j)
  { i++; j++; }
}

С этим классом будут работать через рефлексию. Как передать параметр по ссылке в таком случае?
Вариант
int k=0;
object[] args = new object[] { k, k };
typeof(Test).GetMethod("Proc").Invoke(new Test(), args);
k = (int)(args[0]);

естественно, не годится — получится по значению/результату, а не по ссылке.
Как быть?
Re: Как корректно передать параметр по ссылке при рефлексии в C#?
От: maloi_alex СССР  
Дата: 27.02.13 01:30
Оценка:
Здравствуйте, alxrie, Вы писали:

A>естественно, не годится — получится по значению/результату, а не по ссылке.

A>Как быть?

В MSDN написано, что годится

Если метод или конструктор, представленный этим экземпляром, принимает параметр ref (ByRef в Visual Basic), для вызова метода или конструктора с использованием этой функции не требуются никакие специальные атрибуты. Любой объект этого массива, который не был явно инициализирован с помощью значения, будет содержать значение по умолчанию для данного типа объекта. Для элементов ссылочного типа это значение равно nullNothingnullptrссылка null (Nothing в Visual Basic). Для элементов, тип которых соответствует значению, это значение равно 0, 0,0 или false, в зависимости от заданного типа элемента.

Re[2]: Как корректно передать параметр по ссылке при рефлексии в C#?
От: alxrie  
Дата: 27.02.13 01:46
Оценка:
Здравствуйте, maloi_alex,

Речь несколько об ином. После вызова
int k=0;
new Test().Proc(ref k, ref k);

k будет иметь значение 2 — поскольку параметр передается по ссылке.
После
int k = 0;
object[] args = new object[] { k, k };
typeof(Test).GetMethod("Proc").Invoke(new Test(), args);
k = (int)(args[0])

k будет иметь значение 1.
А надо, чтобы вызов через рефлексию дал тот же результат, что и "прямой" вызов метода.
Re[3]: Как корректно передать параметр по ссылке при рефлексии в C#?
От: maloi_alex СССР  
Дата: 27.02.13 02:27
Оценка:
Здравствуйте, 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#?
От: alxrie  
Дата: 27.02.13 02:55
Оценка:
Здравствуйте, maloi_alex, Вы писали:

M>А у Tes.Proc() сигнатуру нельзя менять?

Увы, нельзя... Настоящий класс уже используется, надо лишь "достучаться" до его методов через рефлексию.
Re[3]: Как корректно передать параметр по ссылке при рефлексии в C#?
От: cvetkov  
Дата: 27.02.13 16:50
Оценка:
нет пути.
... << RSDN@Home 1.2.0 alpha 5 rev. 1539>>
Re[4]: Как корректно передать параметр по ссылке при рефлексии в C#?
От: alxrie  
Дата: 28.02.13 05:58
Оценка:
Здравствуйте, 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 жить стало легче.
Осталось решить указанную Вами проблему с необходимостью объявления делегатов.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.