ФЭНДОМ


Null considered harmful?Править

К написанию этой статьи подтолкнуло разъяснение Дэвида Симмонса о различиях в подходах обработки значений null/nil между C# и S#.

Несмотря на то, что популярные коммерческие языки заявляют о все большей безопасности системы типов, в действительности дела обстоят не так уж радужно. Основных проблем, в принципе, всего две - необходимость приведения типов и наличие null.

Остановимся подробнее на последней.

Как известно, null - это нетипизированное "ничто". Это ссылка на несуществующий объект, которую можно подставить в качестве объекта любого типа. Если по такой ссылке попытаться обратиться или вызвать метод, то возникнет исключение NullReferenceException. Эта концепция берет свое начало от C-ишного (void*)0 или NULL с улучшенной обработкой ошибочных ситуаций.

А причина наличия именно такого null возникает из-за манифестной типизации (manifest typing) и необходимости обозначения отсутствия осмысленного значения в переменной.

В итоге самая правильная на вид программа может выдавать ошибку все по тому же исключению из-за того, что явно или неявно передали null (например, как результат преобразования типов).

 public static string func( Object obj )
 {
   return obj.ToString();
 }
 func(null);

К тому же, то, что null может скрываться под любым типом создает неудобный дуализм - может это реальный объект, а может это null. Тогда для обработки таких ситуаций нужны постоянные проверки

 if (obj != null) { ... } else { ... }

Частично проблема решается введением Null Object Pattern, но это возможно далеко не всегда, да и применяется не так часто.

Если отвлечься от текущего наследия .NET/CLR, то с null, и с семантикой в общем, можно было бы сделать следующее.

  • Сделать null типизированным, т.е. пусть он будет синглтоном класса UndefinedObject.
  • Каждый тип сделать вариантным: SomeType = SomeType! | null, где null обозначается равенство собственно объекту null, SomeType! - это строго экземпляр класса SomeType. В принципе, можно пойти от обратного, и по умолчанию сделать типизацию более строгой, т.е. SomeType? = SomeType | null.
  • В классе UndefinedObject ввести метод
 public virtual Object doesNotUnderstand( string name, object[] params )
 {
   throw new NullReferenceException(name, params);
   return this;
 }
  • При вызове метода через ссылку SomeType! вызывать нужный метод.
  • При вызове метода через ссылку SomeType и если при этом объектом-получателем оказался null, то вызов переадресовать UndefinedObject.doesNotUnderstand, что вызовет нужное исключение.

То есть до этого момента мы получили то же самое, только с улучшенной типизацией за счет наличия SomeType! и типизированного null. Соответственно

 SomeType! var;                  // ошибка
 SomeType! var = null;           // ошибка
 SomeType! var = new SomeType(); // правильно

Далее можно проделать следующее.

У нас имеется класс UndefinedObject. Ни в коем случае не будем делать его sealed (sealed considered harmful? ), а позволим создавать подклассы и переопределять метод doesNotUnderstand.

Создание таким объектов будет осуществляться точно так же с помощью оператора new. При это объект null будет заранее созданным экземпляром UndefinedObject.

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

В качестве примера использования новых возможностей можно реализовать так называемый "глухой объект":

 class DeafObject : UndefinedObject
 {
   public override Object doesNotUnderstand( string name, object[] params )
   {
     return this;
   }
 }

Таким образом, в ответ на вызов любого метода, будет выполняться обработчик doesNotUnderstand, возвращающий самого объекта. Эти вызовы будут все время "проглатываться". Этот объект позволяет легко реализовать Null Object Pattern для любого типа. В отличие от стандартного C#/Java, где для каждого типа объекта придется создавать персональный null-объект.

Среди прочих возможностей - мониторинг обращений к заданному объекту и различные схемы с proxy-объектами.

Владимир Лешкевич

Обнаружено использование расширения AdBlock.


Викия — это свободный ресурс, который существует и развивается за счёт рекламы. Для блокирующих рекламу пользователей мы предоставляем модифицированную версию сайта.

Викия не будет доступна для последующих модификаций. Если вы желаете продолжать работать со страницей, то, пожалуйста, отключите расширение для блокировки рекламы.

Также на ФЭНДОМЕ

Случайная вики