Думаю, интересная и актуальная тема.
Несмотря на то, что популярные коммерческие языки заявляют о все большей безопасности системы типов, в действительности дела обстоят не так уж радужно.
Основных проблем, в принципе, всего две — необходимость приведения типов и наличие 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, и с семантикой в общем, можно было бы сделать следующее.
Сделать null типизированным, т.е. пусть он будет синглтоном класса UndefinedObject.
Каждый тип сделать вариантным: SomeType = SomeType! | null
где null обозначается равенство собственно объекту null,
SomeType! — это строго экземпляр класса SomeType.
В принципе, можно пойти от обратного, и по умолчанию сделать типизацию более строгой,
т.е.
SomeType? = SomeType | null.
В классе UndefinedObject ввести метод
Object doesNotUnderstand( string name, object[] params )
{
throw new NullReferenceException(name, params);
}
При вызове метода через ссылку SomeType! вызывать нужный метод.
При вызове метода через ссылку 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
Владимир.