Null considered harmful?
От: _vovin http://www.pragmatic-architect.com
Дата: 05.03.03 17:03
Оценка: 10 (1)
Думаю, интересная и актуальная тема.

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

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

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

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

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

func(null);


К тому же, то, что null может скрываться под любым типом создает неудобный дуализм — может это реальный объект, а может это null. Тогда для обработки таких ситуаций нужны постоянные проверки
if (obj != null) { ... } else { ... }

вместо того, чтобы сделать свой null-объект и использовать Null Object Pattern.

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

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

  4. При вызове метода через ссылку SomeType! вызывать нужный метод.
  5. При вызове метода через ссылку SomeType и если при этом объектом-получателем оказался null, то вызов переадресовать UndefinedObject.doesNotUnderstand, что вызовет нужное исключение.

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



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

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

Плюс к этому введем метод
UndefinedObject UndefinedObject.registerNullObject(Object anUndefinedSubclass);

который будет создавать синглтон указанного наследника UndefinedObject, которой, в свою очередь, будет вести себя в точности, как и null, только по-своему обрабатывать событие "метод не найден".
Можно также автоматически генерировать UndefinedObjectSubclass.Instance для удобства.

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

Так сказать, пища к размышлению.

--

www.smalltalk.ru
Владимир.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.