Получают ли анонимные методы, переданные действиям, данные по значению или по ссылке?
Я создаю анонимный метод и передаю его в действие, которое будет вызвано позже. Я хотел бы передать некоторые числовые данные (int
) в мой анонимный метод. Требуется ли для копирования данных по значению создавать копии? Или данные будут переданы по значению?
Вот что я думаю, что реализация будет выглядеть, если бы мне пришлось создавать копии:
private void CreateAction()
{
int bus = 4;
CustomObject[] data = new object[16];
int length = 1500;
this.doWorkLater = new Action(() =>
{
var busCopy = bus;
var dataCopy = data;
var lengthCopy = length;
this.WorkMethod(busCopy, dataCopy, lengthCopy);
});
}
Это (приведенный выше код) необходимо для того, чтобы получить length
а также bus
по значению?
В этом случае будет CustomObject[] data
(какой-то класс, который я создал) будет передан по ссылке или по значению?
3 ответа
То, что вы передаете, не является копией по стоимости.
Если вы не собираетесь изменять значения перед выполнением действия, вам не нужно беспокоиться о том, как вы передаете значения. Но нет, они не передаются по значению. Даже если вы вернете действие и Invoke
это из другого метода. значения сохраняются в классе, сгенерированном компилятором. Не нужно беспокоиться об этом
Если вы ожидаете, что данные изменятся до выполнения действия, то вы делаете это неправильно. Подход, который вы используете (Скопировать Value-Type в локальную переменную), должен выполняться вне действия, а не внутри него. Что касается ссылочного типа (Array), даже если вы копируете его в локальную переменную, ссылка копируется, поэтому отражаются любые изменения в локальной переменной копирования.
private void CreateAction()
{
int bus = 4;
CustomObject[] data = new object[16];
int length = 1500;
var busCopy = bus; // a copy of bus
var dataCopy = data; // reference copy
var lengthCopy = length; // a copy of length
this.doWorkLater = new Action(() =>
{
this.WorkMethod(busCopy, dataCopy, lengthCopy);
});
bus = 10; // No effect on the action
length = 1700; // No effect on the action
this.doWorkLater();
}
Это выглядит бессмысленно, но иногда вам может потребоваться скопировать локальную переменную в другую локальную переменную, прежде чем передавать ее анонимному методу. Проверьте этот действительный пример, который исправляет непредвиденное поведение!
Замыкания фиксируют значения, наблюдаемые по ссылке* - обратите внимание, что ваш код не решает проблему в общем случае, даже если весь смысл CreateAction
это создать одно действие, оно будет работать.
private void CreateAction()
{
int bus = 4;
this.doWorkLater = new Action(() =>
{
var busCopy = bus;
this.WorkMethod(busCopy);
});
// if you change local `bus` before call to `doWorkLater` it will not work:
bus = 42;
doWorkLater(); // busCopy is 42.
}
* Он фактически собирает все переменные в созданном компилятором классе и использует ссылку на него для доступа к переменным в методе и замыкании. Таким образом, даже типы значений выглядят так, как если бы они передавались по ссылке.
Это может помочь вам понять, что происходит.
Если вы начнете с этого немного упрощенного класса:
public class Example
{
private void CreateAction()
{
int bus = 4;
object[] data = new object[16];
int length = 1500;
Action doWorkLater = () =>
{
var busCopy = bus;
var dataCopy = data;
var lengthCopy = length;
this.WorkMethod(busCopy, dataCopy, lengthCopy);
};
doWorkLater.Invoke();
}
public void WorkMethod(int bus, object[] data, int length)
{
}
}
... тогда компилятор производит в основном это:
public class Example
{
private void CreateAction()
{
Example.GeneratedClass closure = new Example.GeneratedClass();
closure.parent = this;
closure.bus = 4;
closure.data = new object[16];
closure.length = 1500;
// ISSUE: method pointer
IntPtr method = __methodptr(closure.CreateAction);
new Action((object)closure, method)();
}
public void WorkMethod(int bus, object[] data, int length)
{
}
[CompilerGenerated]
private sealed class GeneratedClass
{
public int bus;
public object[] data;
public int length;
public Example parent;
internal void CreateAction()
{
this.parent.WorkMethod(this.bus, this.data, this.length);
}
}
}
Локальные переменные, захваченные в замыкании, перестают быть локальными переменными для метода и становятся открытыми полями в генерируемом классе.
Теперь все, что вы знаете о C# и классах, применимо.