Arraycollection передается в функцию по значению, а не по ссылке в flex 3

Я хочу установить arrayCollection # 2 = arrayCollection # 1 через функцию в flex 3. Я передаю обе коллекции массивов функции и задаю arrayCollection # 2 = arrayCollection # 1. Однако, похоже, это не передача arrayCollection # 2 по ссылке, потому что после вызова функции arrayCollection # 2 не был изменен. Я понимаю, что он должен быть передан по ссылке и работе, я что-то делаю неправильно? Ниже приведен код:

var AC1:ArrayCollection = new ArrayCollection;
var AC1.addItem(someObject);

var AC2:ArrayCollection = new ArrayCollection;

setAC2(AC1,AC2);
// AC2 is not set to AC1 after the function


private function setAC2(_ac1:ArrayCollection, _ac2:ArrayCollection):void
{
    _ac2 = _ac1;
}

Ответ 1

См. Стратегия оценки.

AS использует "pass by object" / "pass by object-sharing" . То есть "объект" передается (не копия, клон или дубликат), и любые изменения в объекте являются общими.

Однако назначение _ac2 = _ac1 изменяет только значение переменной параметра [function local] и не будет влиять на какие-либо переменные во время вызова функции. Единственное, что прошло, это значения ( "объекты" ), которые являются результатом оценки переменных (или любого произвольного выражения), используемых в вызове функции.

Это связано с тем, что, как указано выше, используемая стратегия - это "передать по объекту" , а не (поскольку в документации указано "передать по ссылке" , что на самом деле означает "передать по значению [ ссылка]" или просто... "передать по объекту" ). То есть термин "передать по ссылке" фактически используется неправильно и, следовательно, запутывает. (Он используется неправильно на нескольких языках и документации. Это тяжелая битва, пытающаяся достичь общего смысла.)

Если бы это было действительно "передать по ссылке" , то присвоение нового значения _ac2 будет распространяться. (Прежде чем писать комментарий о том, как AS "передается по ссылке", см. Ссылку вверху и считайте, что "передать по ссылке" охватывает случай С# out/ref, VB ByRef, TSQL output и С++ ( reference) & - эти понятия не относятся к AS, Javascript или Java). Однако, как правильно отмечено в исходном сообщении (и дополнительном самоответстве), это не так - вывод: AS не поддерживает "передавать по ссылке";, кроме того, документация (смутно) использует термин "передать по ссылке" , чтобы означать "пройти по объекту" / "передать посредством совместного использования объектов".

Существует несколько способов, по которым изменение может распространяться, упорядочиваться по порядку (my):

  • Возвращает новое применимое значение: AC2 = doSomeTransformation(AC1). Это вообще самое чистое. Избегайте побочных эффектов и удивительного кода. Множественные значения могут быть возвращены, если они привязаны к объекту (или массиву) по мере необходимости.

  • Используйте закрытие: doSomeTranformation(AC1, function (newValue) { AC2 = newValue }) где doSomeTransformation может выглядеть так: function doSomeTransformation(_ac1, finished) { ...; finished(_ac1) }. Обычно я использую это только тогда, когда обратный вызов "работает в контексте" самой функции или при написании кода в стиле CPS.

  • Мутировать объект (AS - это "pass by object", в конце концов). Это очень нехорошо, но это сработает. var blah = {AC2: null}; doSomeTransformation(ac1, blah); ...; laterOn(blah.AC2) где doSomeTransformation может выглядеть как function doSomeTransformation(_ac1, b) { ...; b.AC2 = _ac1; }. Не рекомендуется вообще.

Счастливое кодирование.


Применимые выдержки из Стратегия оценки:

"вызов по ссылке": (мой основной аргумент для "вызова по ссылке" используется неправильно, так это то, что он уже имеет четко определенное значение; перегруженный термин, принятый на некоторых языках, таких как AS и Python просто добавляет путаницу)

При оценке посылки (также называемой "пересылкой" ) функция получает неявную ссылку на переменную, используемую как аргумент, а не на копию ее значения. Обычно это означает, что функция может изменять переменную, используемую как аргумент, - то, что будет видно ее вызывающей стороне.

"вызов по объекту" / "вызов путем совместного использования объектов": (но обратите внимание на то, где он признает несогласованность/локализацию этих терминов, термин "вызов по ссылке" часто используется неправильно чтобы подразумевать эту семантику, и "вызов по значению [ссылки]" используется в некоторых контекстах, чтобы также означать одно и то же)

Семантика обмена вызовами отличается от вызова по ссылке в том, что назначения для аргументов функции внутри этой функции не отображаются вызывающему (в отличие от семантики ссылок), так, например, если переменная была передана, невозможно смоделировать присвоение этой переменной в области вызова. Однако, поскольку функция имеет доступ к тому же объекту, что и вызывающий (без копирования), мутации для этих объектов, если объекты изменяемы, в пределах функции видны вызывающему, что может показаться отличным от позывных, семантика значения.

Ответ 2

Аргументы функции в ActionScript передаются по значению, а не по ссылке. Это абсолютно то же самое, что и в Java. Подробнее о здесь.

Ответ 3

Проблема, которую я вижу, это

var AC1.addItem(someObject);

Попробуйте добавить элемент в функцию.

var AC1:ArrayCollection = new ArrayCollection;
var AC2:ArrayCollection = new ArrayCollection;

addItemToArrayCollection( AC1 );
setAC2(AC1,AC2);
// AC2 should be pointing to the ArrayCollection that AC1 is pointing to.

private function setAC2(_ac1:ArrayCollection, _ac2:ArrayCollection):void
{
   _ac2 = _ac1;
}

private function addItemToArrayCollection( arrayCollection:ArrayCollection ):void
{
  arrayCollection.addItem( someObject );
}

После назначения вы можете добавить точку останова и увидеть, что AC2 должен иметь тот же объект, что и AC1.

Ответ 4

Я считаю, что @Constantiner на правильном пути, но я думаю, что его объяснение не хватает деталей; поэтому я попытаюсь объяснить немного глубже; как я понимаю. Я могу исправить меня, если я ошибаюсь.

Как указано в документах:

В ActionScript 3.0 все аргументы переданы по ссылке, поскольку все значения хранятся как объекты. Однако, объекты, принадлежащие примитивному типы данных, которые включают Boolean, Число, int, uint и String, имеют специальные операторы, которые делают их вести себя так, как будто они прошли мимо значение.

Итак, ArrayCollection определенно является объектом, а не примитивным типом, поэтому он должен передаваться по ссылке и действовать так, как будто он передавался по ссылке. Но, какова ваша ссылочная переменная в ArrayCollection. Концептуально это просто указатель на некоторое пространство памяти, содержащее фактические данные коллекции. Вот моя попытка в каком-то искусстве ASCII:

                  |---|
 ac1(variable)--> |   | (ActualArrayCollection1)
                  |---|

                  |---|
 ac2(variable)--> |   | (ActualArrayCollection2)
                  |---|

для повторения, ac1variable является указателем на некоторое пространство памяти. ac2variable - указатель на какое-то другое пространство памяти. Когда вы передаете один из них в метод в качестве аргумента, он передается по ссылке. Итак, внутри метода у вас есть что-то вроде этого:

 ac1(variable)--> |---|
 ac1(argument)--> |   | (ActualArrayCollection1)
                  |---|

 ac2(variable)--> |---|
 ac2(argument)--> |   | (ActualArrayCollection2)
                  |---|

Таким образом, как переменная ac1, так и ac1argument указывают на одно и то же пространство памяти; потому что каждый из них содержит одно и то же значение указателя. Однако ac1variable и ac1argument фактически занимают разные пространства памяти. Они не одинаковы.

Когда метод выполняет эту строку:

_ac2 = _ac1;

Вы получите что-то вроде этого:

 ac1(variable)--> |---|
 ac1(argument)--> |   | (ActualArrayCollection1)
 ac2(argument)--> |---|

 ac2(variable)--> |---|
                  |   | (ActualArrayCollection2)
                  |---|

Когда выполнение метода завершается, два аргумента исчезают, а исходные переменные указателя остаются неизменными. Если вы хотите сделать прямое присваивание, как это внутри метода, вы можете получить доступ к глобальной переменной, используя это ключевое слово. Это должно сделать это:

   this._ac2 = _ac1;

Конечно, это может победить цель инкапсуляции внутри метода, если вы обращаетесь к переменным уровня класса.

Я уверен, что эксперт по дизайну компилятора и такие вещи съедят это на завтрак и выплюнут. Я надеюсь, что мое искусство ASCII совместимо в нескольких браузерах/машинах/операционных системах и т.д.