2013-02-19 2 views
0

Я знаю, что строка в C# может быть «nullable», просто используя null.nullable string in C#

Однако весь смысл обнуляемых типов является то, что я получаю помощь от компилятора [редактирования: иначе, ошибка типа: не могу добавить яблоко бананы]

И с помощью этой «системы типа хак-иш» «нулевая способность зависит от базового типа» ломает любую гарантию, которую я мог бы иметь (что немаловажно, так как это, по-видимому, вся суть использования системы типов в первую очередь.)

Что такое стандартный способ иметь дело с этим в C#, если хотите? Должен ли я просто свернуть собственный класс с нулевым значением?

Редактировать

Позвольте мне перефразировать вопрос:

Что является стандартным способом, в C#, чтобы убедиться, что переменная, аннотированные в обнуляемым, не получает присваивается переменной, вы не аннулировали аннулирование.

То есть: , что является стандартным способом, чтобы иметь для всех типов, именно то, что ? Ключевое слово дает вам значение типа.

+10

Я не понимаю ваш вопрос. –

+0

Почему тег 'f #'? – leppie

+0

Какая конкретная проблема вы сталкиваетесь? – Graham

ответ

0

Все reference types являются общими. Строка является ссылочным типом.

+0

точно. поэтому я задаюсь вопросом, как сделать компилятор явным образом, что это значение потенциально может быть опущено конструкцией VS потенциально null из-за ошибки – nicolas

+0

@nicolas Если тип, который вы назначаете, является ссылочным типом, вы можете назначить значение null.Если это структура (кроме «Nullable », которая имеет специальную поддержку компилятора), то вы не можете. Если у вас есть общий метод и поэтому не знаю тип, вы можете использовать 'default (T)', который будет «null» для всех типов с возможностью NULL или 'where T: class', чтобы гарантировать, что только ссылочные типы допустимые общие аргументы. – Servy

+0

@Servy, если возможность присвоить null - это то, что поставлено на карту для вас, ребята, я понимаю, почему вы не задаете вопрос. Дело в том, чтобы * изолировать * null и отслеживать, куда он может идти, а не наслаждаться свободой нулевого присваивания всего ... – nicolas

3

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

Таким образом, вы можете создать struct как это, чтобы иметь возможность возвращать тип значения:

public struct SurelyNotNull<T> 
{ 
    private readonly T _value; 

    public SurelyNotNull(T value) 
    { 
     if(value == null) 
      throw new ArgumentNullException("value"); 
     _value = value; 
    } 

    public T Value 
    { 
     get { return _value; } 
    } 
} 

Ваш метод, который должен возвращать строку, теперь можно вернуть SurelyNotNull<string>.

Проблема с этим подходом:
Это не работает. Хотя возвращаемое значение метода гарантировано не null, возвращаемое значение SurelyNotNull<T>.Value - нет. На первый взгляд, похоже, что он не будет нулевым. Но это может быть из-за этого:
Каждая структура имеет неявный, открытый, без параметров конструктор, , даже если задан другой конструктор.
Следующий код является действительным, и компилирует с struct сверху:

new SurelyNotNull<string>(); 

Вывод:
В C#, вы не можете добиться того, что вы пытаетесь сделать.
Вы все еще можете использовать этот подход, вам просто нужно понять, что кто-то может использовать этот тип и все еще производить нулевое значение. Быстрое сбой в этом сценарии было бы неплохо добавить проверку к получателю Value и выбросить исключение, если _value имеет значение NULL.

+0

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

+0

Интересно. С некоторым соглашением в коде, я думаю. Хотя это не доказательство пули, это, безусловно, является улучшением по сравнению с тем, что в настоящее время существует (сочетание многих вещей), и я могу использовать компилятор для распространения непустых. – nicolas

+0

@TimothyShields: Конечно. Но это все равно будет исключение во время выполнения, а не ошибка, которую может обнаружить компилятор. И вот что это значит. –

2

Как указал Даниэль Хильгарт, нет пуленепробиваемого способа достижения этого. Мое предложение похоже на его, но с некоторой дополнительной безопасностью. Вы можете решить для себя, если преимущества перевешивают стоимость использования типа обертки во всей вашей программе.

struct NonNull<T> where T : class { 
    private readonly T _value; 
    private readonly bool _isSafe; 

    public NonNull(T value) { 
     if (value == null) 
      throw new ArgumentNullException(); 
     _value = value; 
     _isSafe = true; 
    } 
    public T Value { 
     get { 
      if (_isSafe) return _value; 
      throw new ArgumentNullException(); 
     } 
    } 
    public static implicit operator T(NonNull<T> nonNull) { 
     return nonNull.Value; 
    } 
} 

static class NonNull { 
    public static NonNull<T> Create<T>(T value) where T : class { 
     return new NonNull<T>(value); 
    } 
} 

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

class Program { 
    static void Main(string[] args) { 
     IsEmptyString(NonNull.Create("abc")); //false 
     IsEmptyString(NonNull.Create("")); //true 
     IsEmptyString(null); //won't compile 
     IsEmptyString(NonNull.Create<string>(null)); //ArgumentNullException 
     IsEmptyString(new NonNull<string>()); //bypassing, still ArgumentNullException 
    } 

    static bool IsEmptyString(NonNull<string> s) { 
     return StringComparer.Ordinal.Equals(s, ""); 
    } 
} 

Теперь, это лучше, чем случайные NRE? Может быть. Он может сэкономить много настроек проверки шаблонов. Вам нужно будет решить, стоит ли это для вашей ситуации. В дополнение к поддержке компилятора, например, F #, нет возможности обеспечить безопасную защиту от компиляции, но вы можете (возможно) облегчить безопасность во время выполнения.

Вы можете повышающего голосование этот вопрос на сайте обратной связи с клиентами Microsoft: Add non-nullable reference types in C#

+0

интересно! успокойте меня: у вас было это в backburner в течение долгого времени? – nicolas

+0

Я не использовал это в производственном коде, но в основном потому, что я сделал мир с этим недостатком в C# (и многих других языках).F # предпринял огромный шаг вперед, обратившись к этой ошибке [миллиард долларов] (http://en.wikipedia.org/wiki/Tony_Hoare#Quotations), даже если ее попытка была ограничена необходимостью взаимодействия. Тем не менее, вы можете создать нечто вроде нулевого безопасного огороженного сада для большей части вашего кода F #. – Daniel

+0

Daniel, я надеялся, что у вас есть что-то еще, когда вы разместили свой комментарий по этому вопросу. Я действительно думаю, что «null» является одной из больших проблем на текущих языках. О вашем коде: ваш неявный оператор от 'T' до' NonNull' удаляет * много * безопасности времени компиляции, потому что он позволяет компилировать 'IsEmptyString (null)'. Я думаю, было бы лучше не предоставлять его. –

2

Вы хотите что-то вроде атрибута NotNull. Использование этих параметров даст вам предупреждения и ошибки компиляции, а в некоторых случаях обратную связь IDE о присвоении значениям NULL атрибутам NotNull.

Смотрите этот вопрос: C#: How to Implement and use a NotNull and CanBeNull attribute