Неожиданное исключение на ровном месте

Намедни столкнулся с неожиданным (по крайней мере для меня) исключением на банальном куске кода в Unity. Если убрать все лишнее, то получится примерно вот такой скрипт:

Вышепреведенный код отрабатывал без ошибок, если на игровом объекте висел компонент CanvasGroup . А если этого компонента нет, то код генерирует исключение:

MissingComponentException: There is no ‘CanvasGroup’ attached to the «Button» game object, but a script is trying to access it.
You probably need to add a CanvasGroup to the game object «Button». Or your script needs to check if the component is attached before using it.
TestScript.Start () (at Assets/TestScript.cs:10)

По задумке, само собой, не ожидалось никакого исключения. Какое-то время потребовалось, чтобы понять, почему генерируется это исключение. Я, конечно, был в курсе, что Unity переопределяет оператор сравнения и что использовать операторы ?.  и  ??  с Unity.Object  и его наследников не следует. Но почему-то мне казалось, что переопределенный оператор на что-то влияет, только если мы имее дело с уничтоженным объектом. В моем же коде, по идее, этого не должно было быть. И потому это исключение оказалось неожиданным. Логично было бы предположить, что вместо  MissingComponentException будет генерироваться исключение  NullPointerException . Но, как вы уже поняли, этого не происходит. Согласно документации, метод  GetComponent :

Returns the component of Type type if the game object has one attached, null if it doesn’t.

Этот метод должен вернуть ссылку на компонент или  null , если компонент не найден. А далее приводится пример:

который «подтверждает» ранее описанное поведение. Но если бы это была бы правда, то мой код бы не генерировал исключение. А он генерирует. Дело в том, что метод  GetComponent возвращает ссылку на управляемый компонент, даже если компонент не найден. Иными словами, если запрашиваемый компонент не найден, то все равно возвращается ссылка на управляемый компонент, который не ссылается на настоящий C++ компонент и переопределенный оператор сравнения с  null  не срабатывает.  Создается ощущение, что вернулся  null . Это легко подтвердить, например, таким кодом:

В результате выполнения этого кода мы увидим:

UnityEngine.CanvasGroup
UnityEngine.Debug:Log(Object)
TestScript:Start() (at Assets/TestScript.cs:9)

Также стоит отметить, что, если сбилдить приложение, то тестовый код выполнится без ошибок, а ненайденный компонент будет добавлен. Происходит это потому, что в run-time  GetComponent будет возвращаться настоящий  null , и все выполнится, как и задумывалось. Т.е. подмена возвращаемого значения работает только в редакторе Unity. Само собой, не стоит использовать операторы  ?.  и  ??  с  Unity.Object  и его наследниками в реальном коде.

Добавить комментарий