Необнуляемые ссылочные типы - PullRequest
12 голосов
/ 28 марта 2009

Я разрабатываю язык и задаюсь вопросом, разумно ли по умолчанию делать ссылочные типы не обнуляемыми и использовать "?" для обнуляемых значений и ссылочных типов. Есть ли проблемы с этим? Что бы вы сделали по этому поводу:

class Foo {
    Bar? b;
    Bar b2;
    Foo() {
        b.DoSomething(); //valid, but will cause exception
        b2.DoSomething(); //?
    }
}

Ответы [ 7 ]

16 голосов
/ 28 марта 2009

Моя нынешняя философия языкового дизайна заключается в том, что программист должен запрашивать обнуляемость, а не указывать по умолчанию для ссылочных типов (в этом я согласен с Тони Хоаром - Google для его недавнего выступления на QCon).

В этом конкретном примере, с необнуляемым b2, он даже не пройдет статические проверки: консервативный анализ не может гарантировать, что b2 не равен NULL, поэтому программа не имеет семантической значимости.

Мой дух достаточно прост. Ссылки являются косвенным указателем на некоторый ресурс, который мы можем пройти, чтобы получить доступ к этому ресурсу. Обнуляемыми ссылками являются или дескриптор косвенного обращения к ресурсу, или уведомление о том, что ресурс недоступен, и никто не знает заранее, какая семантика используется. Это дает либо множество проверок заранее (это ноль? Нет? Yay!), Либо неизбежный NPE (или эквивалентный). В наши дни большинство ресурсов программирования не ограничено или не связано с какой-либо конечной базовой моделью - нулевые ссылки, проще говоря, являются одним из ...

  • Лень: «Я просто обнюхал здесь». Что, честно говоря, я не слишком сочувствую
  • Путаница: «Я еще не знаю, что сюда вставить». Как правило, это также наследие более старых языков, когда вам приходилось объявлять имена ресурсов, прежде чем вы знали, что это за ресурсы.
  • Ошибки: «Все пошло не так, вот NULL». Таким образом, улучшенные механизмы сообщения об ошибках необходимы на языке
  • Отверстие: «Я знаю, что скоро у меня что-то будет, дай мне заполнитель». Это имеет больше достоинств, и мы можем придумать способы борьбы с этим.

Конечно, решение каждого из случаев, которые в настоящее время обслуживает NULL, с лучшим лингвистическим выбором не является небольшим подвигом, и может добавить больше путаницы, что оно помогает. Мы всегда можем перейти к неизменным ресурсам, так что NULL только в его полезных состояниях (ошибка и дыра) не слишком полезен. Тем не менее, императивные технологии остаются здесь, и я искренне рад, что это делает поиск лучших решений в этом пространстве стоящим.

11 голосов
/ 28 марта 2009

Единственным разумным выбором является наличие необязательных значений по умолчанию для ссылочных типов. Мы страдаем от языков и сред выполнения, которые испортили это; ты должен поступать правильно.

9 голосов
/ 29 марта 2009

Эта функция была в Spec # . Они по умолчанию обнулились ссылками и использовали! чтобы указать не обнуляемые. Это потому, что они хотели обратной совместимости.

На языке моей мечты (из которых я, вероятно, был бы единственным пользователем!) Я бы сделал такой же выбор, как и вы, по умолчанию не обнуляемый.

Я бы также сделал незаконным использование. оператор обнуляемой ссылки (или чего-либо еще, что могло бы разыменовать его). Как бы вы их использовали? Вы должны сначала преобразовать их в необнуляемые. Как бы вы это сделали? Проверяя их на ноль.

В Java и C # оператор if может принимать только тестовое выражение bool. Я бы расширил его, чтобы принять имя переменной ссылки, допускающей значение NULL:

if (myObj)
{
    // in this scope, myObj is non-nullable, so can be used
}

Этот специальный синтаксис был бы неудивителен для программистов на C / C ++. Я бы предпочел специальный синтаксис, подобный этому, чтобы прояснить, что мы делаем проверку, которая изменяет тип имени myObj в пределах ветви истинности.

Я бы добавил еще немного сахара:

if (SomeMethodReturningANullable() into anotherObj)
{
    // anotherObj is non-nullable, so can be used
}

Это просто дает имя anotherObj результату выражения слева от into, поэтому его можно использовать в области действия, где оно действительно.

Я бы сделал то же самое для оператора ?:.

string message = GetMessage() into m ? m : "No message available"; 

Обратите внимание, что string message не имеет значения NULL, но таковы два возможных результата теста выше, поэтому присваивание является значением.

А потом, возможно, немного сахара для предположительно распространенного случая замены значения на ноль:

string message = GetMessage() or "No message available";

Очевидно, что or будет справедливо применяться только к обнуляемому типу на левой стороне и не обнуляемому на правой стороне.

(у меня также было бы встроенное понятие владения полями экземпляра; компилятор генерировал бы метод IDisposable.Dispose автоматически, а синтаксис ~Destructor использовался бы для увеличения Dispose, точно так же, как в C ++ /CLI.)

Spec # имел другое синтаксическое расширение, связанное с ненулевыми значениями, из-за проблемы обеспечения правильной инициализации ненулевых элементов во время построения:

class SpecSharpExampleClass
{
    private string! _nonNullableExampleField;

    public SpecSharpExampleClass(string s)
        : _nonNullableExampleField(s) 
    {

    }
}

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

4 голосов
/ 28 марта 2009

Посмотрите на предложение оператора Элвиса для Java 7. Это делает нечто похожее в том, что он инкапсулирует проверку на ноль и отправку метода в одном операторе с указанным возвращаемым значением, если объект является нулевым , Следовательно:

String s = mayBeNull?.toString() ?: "null";

проверяет, является ли String s нулевым, и возвращает строку «null», если это так, и значение строки, если нет. Пища для размышления, возможно.

3 голосов
/ 28 марта 2009

Несколько примеров похожих функций на других языках:

Существует также Nullable<T> (из C #), но это не очень хороший пример из-за различий в обращении типов сравнения и значений.

В вашем примере вы можете добавить условный оператор отправки сообщения, например,

b?->DoSomething();

Чтобы отправить сообщение на b, только если оно не равно нулю.

0 голосов
/ 27 сентября 2012

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

Например

/// "translation unit 1"

#set nullable
{ /// Scope of default override, making all declarations within the scope nullable implicitly
     Bar bar; /// Can be null
     non-null Foo foo; /// Overriden, cannot be null
     nullable FooBar foobar; /// Overriden, can be null, even without the scope definition above 
}

/// Same style for opposite

/// ...

/// Top-bottom, until reset by scoped-setting or simply reset to another value
#set nullable;

/// Nullable types implicitly

#clear nullable;

/// Can also use '#set nullable = false' or '#set not-nullable = true'. Ugly, but human mind is a very original, mhm, thing.

Многие люди утверждают, что дать всем то, что они хотят, невозможно, но если вы разрабатываете новый язык, попробуйте новые вещи. Тони Хоар представил концепцию null в 1965 году, потому что он не мог сопротивляться (его собственные слова), и с тех пор мы платим за это (также, по его собственным словам, человек сожалеет об этом). Дело в том, что умные, опытные люди совершают ошибки, которые стоят остальной части нас, не советуйтесь ни с кем на этой странице, как если бы это была единственная правда, включая мою. Оцените и подумайте об этом.

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

0 голосов
/ 28 марта 2009

Я думаю, что нулевые значения хороши: они ясно указывают на то, что вы сделали что-то не так. Если вам не удалось инициализировать ссылку где-либо, вы получите немедленное уведомление.

Альтернативой может быть то, что значения иногда инициализируются значением по умолчанию. В этом случае логические ошибки намного сложнее обнаружить, если вы не включите логику обнаружения в эти значения по умолчанию. Это было бы то же самое, что просто получить исключение нулевого указателя.

...