PDA

Просмотр полной версии : Обнуление при конверсии типов.


Fogflasher
10.12.2013, 14:20
Есть следующая функция:

public static function playInto(mc:DisplayObjectContainer):void
{
var cont:MovieClip = mc as MovieClip;
trace("mc = ", mc, mc.name);
trace("cont = ", cont, cont.name);
}

Первый трэйс отрабатывает нормально (значит в функцию приходит не null).
А вот второй выдает ошибку:
TypeError: Error #1009: Cannot access a property or method of a null object reference.

Если перепрописать первую строчку вот так:
var cont:MovieClip = MovieClip(mc);

То возникает другая ошибка:
TypeError: Error #1034: Type Coercion failed: cannot convert Clip_container@2df5401 to flash.display.MovieClip.

И как же это понимать, выходит нельзя что-ли вообще конвертировать DOC в MovieClip. Или может быть специфика какая-то влияет.

Akopalipsis
10.12.2013, 14:27
Вообще у меня Ваш код работает. Возможно ошибка из-за обращения в статическом методе к не статическим свойствам...
package
{
import flash.display.DisplayObjectContainer;
import flash.display.MovieClip;
import flash.display.Sprite;

public class DOCTest extends Sprite
{
private var test:String = 'test';
public function DOCTest()
{
var mc:MovieClip = new MovieClip();
mc.name = 'mc';
DOCTest.test(mc);
}
private static function test(doc:DisplayObjectContainer):void
{
var mc:MovieClip = doc as MovieClip;
trace(mc.name);//mc
}
}

}

Fogflasher
10.12.2013, 14:50
Но у вас mc изначально MovieClip, это может влиять на успешность, наверное.
А у меня ссылка на объект которая прилетает в функцию, и ссылка, скорее всего, указывает на объект DOC.

Хотя, точно не знаю.
Объект, который "прилетает" в функцию - это экземпляр класса, который расширяет Sprite.
Я вообще думал у него тип Sprite (ну или MovieClip).

А похоже... стоп, а почему я решил, что это DOC, может это и правда DC...
Можно ли кстати как-то определить на лету, "чьим наследником" является данный объект?
Он так и остается Sprite? Или типность может считатся и как-то иначе, вверх по иерархии уходя.

Wolsh
10.12.2013, 14:53
Вообще у меня Ваш код работаетНу так если мувиклип передавать, то конечно))) Автор же видимо считает что можно любой контейнер ПЕРЕДЕЛАТЬ в МувиКлип.
Во-первых, as это не конверсия а кастинг, то есть "проба на роль". Возвращается null, если испытуемый не может играть роль, то есть не является сам и не содержит в цепочке наследования нужный класс. При этом не выбрасывается исключение (не генерится ошибка, Error). Ваш Error не при кастинге, а при попытке вызвать у null методы, которых у него нет.
Конверсия это MovieClip(mc). Если конверсия невозможна (по тем же причинам что и выше), то выбрасывается исключение "несоответствие типов". Вообще, никакого волшебного преобразования одного типа в другой нет, если Вы на такое надеялись. Не сделать МувиКлип из других классов. И кастинг, и конверсия в итоге говорят только "можно рассматривать данный экземпляр как экземпляр класса МувиКлип". Но с самим объектом, естественно, ничего при этом не происходит.

Akopalipsis
10.12.2013, 14:54
package
{
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.MovieClip;
import flash.display.Sprite;

public class DOCTest extends Sprite
{
private var test:String = 'test';
public function DOCTest()
{
var mc:MovieClip = new MovieClip();
var sprite:Sprite = new Sprite();
DOCTest.test(mc);
DOCTest.test(sprite);
}
private static function test(target:DisplayObject):void
{
if (target is MovieClip)
{
var mc:MovieClip = target as MovieClip;
}else if (target is Sprite)
{
var sprite:Sprite = target as Sprite;
}
}
}

}

Wolsh
10.12.2013, 15:03
Akopalipsis, (исправьте "doc" на "target"), и что дальше с этим делать?
if (mc != null)... if (sprite != null)
так что-ли? А сразу нельзя было?))

Fogflasher
10.12.2013, 15:04
Wolsh, интересно, спасибо за разьяснения.
Не знал что это всего лишь роль, думал флэш насильно трансмутирует объект, засовывая его в прокрустово ложе.

Akopalipsis, да точно, можно же и так, а то я уже было полез искать хитрые методы идентификации класса.
Добавил такой блок проверки:
if (mc is MovieClip)
{
trace("is MovieClip");
}
else if (mc is DisplayObject)
{
trace("is DisplayObject");
}
else if (mc is DisplayObjectContainer)
{
trace("is DisplayObjectContainer");
}
else if (mc is Sprite)
{
trace("is Sprite");
}
else
{
trace("Holly crap!");
}

И получилось, что это DisplayObject.
Вы были правы в первом (удаленном) камменте.

А вот почему так..?
Он же extends Sprite. Или все объекты, которые попали в список отображения, автоматически меняют типа на DisplayObject?

strangedk
10.12.2013, 15:05
Помнится, однажды у меня были проблемы при попытке конвертировать экспортированный MovieClip в собственно MovieClip. Ругался на несовместимость типов и всё тут.

Точно не вспомню как порешалось, но была обнаружена странность: Flash считал что MovieClip с одним кадром это Sprite.

Использовался и Flash Builder (для кастинга) и Flash CS (для экспорта swf).

Akopalipsis
10.12.2013, 15:12
и что дальше с этим делать?
Вот тут я не понял... Если в методе ожидать конкретно mc, то да, можно сделать как Вы написали - if (mc != null). Но скорее всего в методе ожидаются любые классы, которые нужно присвоить на некоторое время какой то переменной и для этого нужно проверить лишь принадлежность к MC либо Sprite. Ну или в крайнем случаи if (target is MovieClip&&target!=null)

Fogflasher
10.12.2013, 15:13
strangedk, кстати у меня похожий случай. (Использую пока только Flash IDE, к слову).

Этой функции передается объект, класс которого в библиотеке создан как MovieClip.
Но он имеет один кадр, и я для него написал еще и кустомный класс, в котором много всего.
Внутри этого кустомного класса, он кстати расширяет Sprite, что... является наверное некоторым косячком : ))
Возможно не стоило вообще ему extends дописывать, ведь в библиотеке этот extends (если я всё правильно понимаю) уже назначен на MovieClip.

Wolsh
10.12.2013, 15:15
Никто никуда никогда и никак не трансмутирует. Сам по себе объект это область памяти с данными, и он остается таким как есть, всегда одного своего типа. Другие типы могут быть только у переменных, хранящих ссылки. Если Вы объявили тип переменной как Спрайт, она будет "выглядеть" как Спрайт. Если Вы засунете в нее ссылку на МувиКлип, никакой ошибки итак не будет. Просто обращаясь к этой переменной, Вы будете иметь доступ только к свойствам и методам Спрайта, а специфические свойства и методы Мувиклипа (а тем более ваших экспериментов по расширению) доступны не будут. Однако, Вы всегда можете кастануть этот объект обратно в МувиКлип или кастомный наследник мувиклипа, используя ссылку в той же переменной. То есть
var sprite:Sprite = new MovieClip();
sprite.gotoAndStop(0) // Error, обращение к неопределенному в Спрайт методу gotoAndStop
var mc:MovieClip = sprite as MovieClip;
mc.gotoAndStop(0); // легко, ведь объект действительно мувиклип

Добавлено через 5 минут
В IDE нет ручного механизма для создания спрайтов. Если при создании объекта ручками вписать класс Спрайт, то будет создан спрайт (и у иконки в библиотеке другой цвет будет). Ну а если Вы сами в классе написали, что это спрайт, то чему удивляться? Я бы не удивился, если бы ИДЕ при компиляции автоматически делала спрайты из мувиклипов без таймлайна... Если конечно никто не пытается использовать их в коде как мувиклипы)

strangedk
10.12.2013, 15:31
он кстати расширяет Sprite, что... является наверное некоторым косячком
Если у ваш класс явно в коде наследник от Sprite, то Flash CS даже в библиотеке этот символ помечает зеленым (а не фиолетовым) цветом.

Сейчас нет возможности попробовать, но подозреваю что подобное Flash CS делает с MovieClip'ами с одним кадром, без класса-предка в коде. Но только как-то по тихому.

Akopalipsis
10.12.2013, 16:03
Добавил такой блок проверки:
Так не правильно. Всё что на экране, это наследники DO и по Вашему условию, всё что не MC это DO.
То есть Вы смешиваете и Sprite и Bitmap..

Fogflasher
10.12.2013, 17:36
Wolsh, а ведь наверное и заморочки с иерархией всегда должны быть? Ведь можно кастовать вниз, а можно вверх (причем через одного, двух...).
Так как MovieClip наследник Sprite, то кастовать снизу-вверх наверное всегда сопряжено с побочными эффектами. Хотя тут еще надо поразмышлять над вашим примером, да. Сочетание: переменная одного типа (класса) - но создается как экземпляр другого типа (класса) несколько смущает.

strangedk, ну вот в моем портабельном флэше ничего зелененьким не подсвечивается. Даже если в галках явно вписать Sprite.
Надо бы перейти на современную версию, да всё как-то не cоберусь, лол.

Akopalipsis, я так полагаю, тут есть два варианта, что нужно считать "правильным".
С точки зрения, если я хочу получить изначальный тип объекта, то это неправильно.
А если я хочу получить кастанутый тип объекта, то выходит что правильно.

Прямого ответа здесь не было, но подозреваю, что помещение объекта в список отобржаения, это автоматический кастинг: mc as Display Object
(кстати, а почему не DisplayObjectContainer, ну да ладно).

Но как быть с первым случаем, который был бы по вашему правильный.
Какой для этого код нужен, чтобы некастанутый тип вычислить?

Akopalipsis
10.12.2013, 17:52
это автоматический кастинг: mc as Display Object
я не знаю что такое каст, но к типу DO не чего не приводится, он уже есть во всем отображении, он родитель всего, что на экране. Проверять что то из отображаемого на DO нет смысла.
Но как быть с первым случаем, который был бы по вашему правильный.
Какой для этого код нужен, чтобы некастанутый тип вычислить?
Тут я не могу ответить, я не знаю что такое кастанутый и не понимаю, как Вы можете в собственном коде ошибиться с типами классов.. Если Вам просто проверить является ли этот обьект типом "...", то is, если Вам нужно создавать с нужным типом обьекты, то flash.utils смотрите, если какое то извращение, то интерфейсы. Вроде всё что можно сделать с типами я перечислил.

Добавлено через 14 минут
Вообще, если Вы не знаете какой будет тип, но уверенны, что он DO и Вы хотите использовать стандартные свойства, то можно вот так -
var object:DisplayObject=target;

in4core
10.12.2013, 18:17
Akopalipsis - do зарезервированное слово. Поэтому ВОТ-ТАК нельзя :)

Akopalipsis
10.12.2013, 18:22
in4core Спасибо! Не знал.. хотя сейчас вспоминаю, то есть do..while..

Wolsh
10.12.2013, 19:46
Хотя тут еще надо поразмышлять над вашим примером, да.Размышляйте. У Вас такая каша в голове, что трудно даже отвечать что-то вразумительное, ибо невозможно сходу понять, что и как Вы себе понимаете.
Смысл переменной в чем? В том, что она может менять свое значение в рантайме. Это значит, что компилятор не может знать, какие ЗНАЧЕНИЯ она будет принимать в процессе воспроизведения ролика. Значение — это [ссылка на] область памяти, в рантайме. Компилятор не в курсе, и не может ничего проверить и предотвратить ошибку. Компилятор/код работает не с областями памяти, а с переменными. Этакими абстракциями, представляющими будущий "живой" объект в рантайме. У переменных есть только одно свойство: Тип. И компилятор рассматривает Тип как нечто строгое и незыблемое. Он, естесственно, не может знать, что в переменную типа Спрайт передадут именно МувиКлип (из моего примера конечно это видно, но переменные могут быть параметрами функции, и кто и что передаст в рантайме в эту функцию, никакой супермодный компилятор знать не может). Компилятор проверяет код; и в коде для него есть только данный Тип, указанный в объявлении переменной. Он и в мыслях не допускает, что Спрайт может оказаться МувиКлипом. Это раз.
Наследование называется расширением не просто так. Наследуя какой-то Класс, Вы его расширяете, добавляя новые свойства и методы, которых в суперклассе не было вообще. Например есть суперкласс Транспорт, а Вы создаете расширяющий класс Зенитка. Тут есть некоторый нюанс русского языка — нам кажется что Зенитка не "шире", а наоборот "уже" чем понятие "Транспорт вообще". Можно использовать понятия "абстрактный класс Транспорт" и "конкретный класс Зенитка". А под "расширением" понимается именно увеличение числа "членов класса" — методов и свойств. Класс становится больше, шире. У Зенитки ко всем свойствам Транспорта добавляются конкретные свойства Зенитки — количество стволов, дальнобойность, скорострельность, стрелок, кол-во снарядов, и методы типа стрелять().
Так вот, Вы можете хранить ссылку на зенитку в переменной типа Транспорт, потому что Зенитка является Транспортом. Одновременно. Она И транспорт, И зенитка. Вы можете вызывать у объекта Зенитка все методы и свойства Транспорта, поэтому она может играть роль Транспорта.
Но НЕ наоборот. Вы НЕ можете хранить в переменной типа Зенитка обычный Транспорт, потому что он НЕ может играть роль зенитки — у него нет методов и свойств конкретного класса Зенитка. Это два.
Спрайт наследует (расширяет) Контейнер, тот расширяет ИнтерактивОбжект, а ИО расширяет ДисплейОбжект, который расширяет EventDispatcher и наконец простой Обжект. Следовательно Спрайт содержит все эти типы, и может играть роль любого из них. МувиКлип расширяет Спрайт, и тоже может использоваться как любой класс из этой цепочки. Но ни один класс из этой цепочки не может использоваться ВМЕСТО МувиКлипа.
Когда в приемнике функции Вы создаете переменную с общим типом DosplayObject, это означает, что в функцию может быть отдан ЛЮБОЙ наследник ДО — Битмап, Мувиклип, Шейп, ТекстФилд и тп. Но функция будет рассматривать объект только как ДО, ничего не зная о его расширенных свойствах. Чтобы получить к ним доступ, надо кастовать объект к нужному типу. Это происходит в рантайме, где собственно и существует объект. Для компилятора же есть только тип переменной, в которую его записали, а о конкретном типе объекта он ничего не знает. Поэтому используется механизм проверки is, или кастинг as. В результате кастинга Вы должны создать НОВУЮ переменную уже с конкретным типом, и далее компилятор сможет спокойно с ней работать. Но в рантайме, естественно, тип может не совпасть, если передали ТекстФилд, а кастуем к Битмап, кастинг выдаст нулл. Обработать эту ситуацию проверкой результата кастинга — новой переменной — Ваша забота. Кастинг просто более "демократичен" чем конверсия, не кидается сразу Ошибкой (потому что там нет никакой ошибки по смыслу). У Вас в любом случае получается создать переменную нужного типа, вот только ее значение может оказаться null, как если бы написали просто var s:Sprite; как часто и делают совершенно сознательно.

Fogflasher
12.12.2013, 12:34
Спасибо, многие элементы мозайки я вобщем так и представлял, но теперь картинка более целостно выглядит.
Просто архитектура моего проекта-эксперимента достаточно накручена, что усложняет понимание.

Хотел еще кое-что уточнить. Вот например, если в изначальном примере, тип параметра метода оставить без указания чего либо: public static function playInto(mc):void

То внутри этого метода срабатывает условие:
if (mc is DisplayObject)
{
trace("is DisplayObject");
}

При этом, ни "if is Sprite", ни "if is MovieClip" не срабатывают.

Я говорю именно о mc, то есть ни о каких дополнительных локальных переменных метода речь не идет.
Выходит, даже если нигде нет ни кастинга, ни конверсии, он почему-то "превращается" в DisplayObject.

Если я все правильно понял в вашем последнем камменте, такого не должно быть.
Если объект в метод подан как Sprite (хотя есть вероятность, что это все-таки MovieClip, в силу описанного выше двуликого косяка), то он и должен внутри метода определятся как Sprite, а это не так.

Возможно, тут еще влияют дополнительные ньюансы:
1. Этот mc, не добавляется программно изначально, он имеет инстанс-нэйм внутри "основного мувиклипа" в библиотеке, и добавляется в дисплэй лист автоматически, во время добавления туда этого "основного мувиклипа".

2. Может в IDE есть специфика, связанная с библиотечными объектами, которой нет в Builder'e.

Wolsh
12.12.2013, 13:13
Я говорю именно о mc, то есть ни о каких дополнительных локальных переменных метода речь не идет.Именно о них речь и идет. То, что Вы отключили компилятору строгий режим и он позволяет Вам не указывать Тип переменной в параметре, не означает, что это не локальная переменная метода О__о
Просто компилятор укажет ее тип как :*

Выходит, даже если нигде нет ни кастинга, ни конверсии, он почему-то "превращается" в DisplayObject.Передаваемый ОБЪЕКТ является ДисплейОбжектом. Этого не может изменить ни кастинг, ни конверсия.
var s:Sprite = new MovieClip();
trace(s is MovieClip);

Если объект в метод подан как SpriteОбъект "подан" как он есть. Он может быть "принят" в переменную супертипа, да. Сотый раз повторяю, с объектом НИЧЕГО НЕ ПРОИСХОДИТ. Объект — не переменная. Переменная — не объект.

При этом, ни "if is Sprite", ни "if is MovieClip" не срабатывают.Есть еще Shape. Bitmap. TextField. SimpleButton. Все перечислять? Они все — is DisplayObject.

Akopalipsis
12.12.2013, 14:12
package
{
public class SuperClass
{

public function SuperClass()
{

}

}

}
package
{
public class SubClass extends SuperClass
{

public function SubClass()
{

}

}

}
package
{
/**
* ...
* @author ...
*/
public class SubSubClass extends SubClass
{

public function SubSubClass()
{
super();

}

}

}
package
{
import flash.display.Sprite;

public class Main extends Sprite
{

public function Main()
{
var testClass:SuperClass = new SubSubClass();
trace(testClass is SuperClass,//true
testClass is SubClass, //true
testClass is SubSubClass);//true
}

}

}
Наследование началось от класса SuperClass, который расширял SubClass, который расширял SubSubClass.
Не зная с каким классом придётся иметь дело, можно указать супер тип, каковым является SuperClass.
var testClass:SuperClass = new SubSubClass();
И теперь если посмотреть по трейсам, то везде true, по тому что SubSubClass имеет типы и SubClass и SuperClass. Так же всё что отображается имеет один супер тип DisplayObject, он у них лежит в самой основе.

Fogflasher
12.12.2013, 14:44
Wolsh, То, что Вы отключили компилятору строгий режим и он позволяет Вам не указывать Тип переменной в параметре, не означает, что это не локальная переменная метода О__о
Просто компилятор укажет ее тип как :*

Нет, нет, галочка Strict Mode у меня в настройках есть. И тем не менее такой синтаксис проходит, даже без предупреждений. Но то, что параметер - это тоже локальная переменная, в этом я не был уверен, теперь понятно.

Передаваемый ОБЪЕКТ является ДисплейОбжектом.

Кхм, я как-бы извиняюсь за тормознутость, но... как же он может являтся ДисплейОбжектом, если я его создавал в библиотеке как МувиКлип?
Разве это не более важно? Первичен тот тип, под которым объект создан, разве нет?

Тогда уж ДисплейОбжект - это "роль" (или может быть именно это имелось ввиду под словом "является"), а не тот тип, который у него был изначально.
А то ведь можно рассуждать и так - этот мувик в первую очередь Object, и это первично, а потом уже всё остальное.

Добавлено через 10 минут
Akopalipsis, Фух, спасибо, теперь я понял.

Конешно, надо было if'ы отдельно делать, без взаимоисключений.

Теперь видно, что передаваемый клип ЯВЛЯЕТСЯ одновременно несколькими классами в цепочке.

is DisplayObject
is DisplayObjectContainer
is Sprite

Akopalipsis
12.12.2013, 15:18
клип ЯВЛЯЕТСЯ одновременно несколькими классами в цепочке.
Он имеет сразу несколько ТИПОВ - SuperClass, SubClass и SubSubClass.
Но если указать для testClass тип супер тип, то будут доступны лишь свойства и методы, которые есть в SuperClass.
var testClass:SuperClass = new SubSubClass();
Вот из хелпа:
Наследование MovieClip Sprite DisplayObjectContainer InteractiveObject DisplayObject EventDispatcher Object
То есть, когда Вы подписываете MC на какое то событие, Вы делаете это только по тому, что он расширяет-наследуется от EventDispatcher и так далее..