2009-11-06 3 views
23

Так что я был заинтригован ?? оператора, но до сих пор не могут его использовать. Обычно я думаю об этом, когда я делаю что-то вроде:Насколько полезен C#? оператор?

var x = (someObject as someType).someMember; 

Если SomeObject является действительным и someMember имеет нулевое значение, я мог бы сделать

var x = (someObject as someType).someMember ?? defaultValue; 

, но почти всегда я с проблемами, когда SomeObject имеет нулевое значение, а также ?? не помогает мне сделать это чище, чем делать нулевую проверку.

Что вы использовали для ребята? в практических ситуациях?

+9

FYI: ?? называется нулевым коалесцирующим оператором. –

+0

Да, именно то, что я думаю. – Ruben

+0

Я думаю, что это должна быть «вики сообщества». –

ответ

26

Я согласен с вами в том, что ?? оператор, как правило, ограниченного использования. Полезно предоставить значение возврата, если что-то имеет значение NULL, но не является полезным для предотвращения исключения Null Reference в случаях, когда вы хотите продолжить сверление свойств или методов, null ссылка.

ИМХО, что нужно гораздо больше, чем ??? является оператором «null-dereference», который позволяет вам объединять длинные цепи свойств и/или методов вместе, например. a.b().c.d().e без проверки каждого промежуточного шага для нулевого значения. Язык Groovy имеет Safe Navigation operator, и это очень удобно.

К счастью, похоже, что команда C# знает об этом недостатке. См. Это connect.microsoft.com suggestion для команды C#, чтобы добавить оператор нулевой разницы на язык C#.

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

a? .b? .c ?? d

Значение, если какой-либо из a, a.b или a.b.c имеет значение , вместо этого использовать d вместо d.

Мы рассматриваем это для будущего выпуска , но его не будет в C# 4.0.

Еще раз спасибо,

Мадс Торгерсен, C# Язык PM

Если вы хотите эту функцию в C#, добавить свои голоса в suggestion на подключения сайте! :-)

Один из способов я использую, чтобы обойти отсутствие этой функции является метод расширения, как то, что описано в this blog post, чтобы такой код:

string s = h.MetaData 
      .NullSafe(meta => meta.GetExtendedName()) 
      .NullSafe(s => s.Trim().ToLower()) 

Этот код возвращает либо h.MetaData.GetExtendedName().Trim().ToLower() или возвращает null, если h, h.MetaData или h.MetaData.GetExtendedName() - null. Я также расширил это, чтобы проверить нулевые или пустые строки, или пустые или пустые коллекции. Вот код, который я использую для определения этих методов расширения:

public static class NullSafeExtensions 
{ 
    /// <summary> 
    /// Tests for null objects without re-computing values or assigning temporary variables. Similar to 
    /// Groovy's "safe-dereference" operator .? which returns null if the object is null, and de-references 
    /// if the object is not null. 
    /// </summary> 
    /// <typeparam name="TResult">resulting type of the expression</typeparam> 
    /// <typeparam name="TCheck">type of object to check for null</typeparam> 
    /// <param name="check">value to check for null</param> 
    /// <param name="valueIfNotNull">delegate to compute if check is not null</param> 
    /// <returns>null if check is null, the delegate's results otherwise</returns> 
    public static TResult NullSafe<TCheck, TResult>(this TCheck check, Func<TCheck, TResult> valueIfNotNull) 
     where TResult : class 
     where TCheck : class 
    { 
     return check == null ? null : valueIfNotNull(check); 
    } 

    /// <summary> 
    /// Tests for null/empty strings without re-computing values or assigning temporary variables 
    /// </summary> 
    /// <typeparam name="TResult">resulting type of the expression</typeparam> 
    /// <param name="check">value to check for null</param> 
    /// <param name="valueIfNotNullOrEmpty">delegate to compute non-null value</param> 
    /// <returns>null if check is null, the delegate's results otherwise</returns> 
    public static TResult CheckNullOrEmpty<TResult>(this string check, Func<string, TResult> valueIfNotNullOrEmpty) 
     where TResult : class 
    { 
     return string.IsNullOrEmpty(check) ? null : valueIfNotNullOrEmpty(check); 
    } 
    /// <summary> 
    /// Tests for null/empty collections without re-computing values or assigning temporary variables 
    /// </summary> 
    /// <typeparam name="TResult">resulting type of the expression</typeparam> 
    /// <typeparam name="TCheck">type of collection or array to check</typeparam> 
    /// <param name="check">value to check for null</param> 
    /// <param name="valueIfNotNullOrEmpty">delegate to compute non-null value</param> 
    /// <returns>null if check is null, the delegate's results otherwise</returns> 
    public static TResult CheckNullOrEmpty<TCheck, TResult>(this TCheck check, Func<TCheck, TResult> valueIfNotNullOrEmpty) 
     where TCheck : ICollection 
     where TResult : class 
    { 
     return (check == null || check.Count == 0) ? null : valueIfNotNullOrEmpty(check); 
    } 
} 
+0

Мне нравится идея вспомогательного метода, но его грустно, что это должен быть такой хак. – captncraig

+0

Да. Если вы хотите получить это на C#, есть простой способ ускорить процесс: проголосовать за предложение на https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=432830! :-) –

+0

404 на ссылке «безопасный-нуль-разыменовать оператора». А также на ссылке на подключение к Microsoft. –

7

?? фактически является оператором нулевой проверки.

Причина ваш код попадает в неприятности, что если «SomeObject равна нулю, то .someMember это свойство на несуществующий объект Вы должны проверить SomeObject на нуль первого

EDIT:..

Я должен добавить, что да, я действительно очень полезен, особенно при обработке ввода базы данных, особенно из LINQ, но также и в других случаях.

22

Обычно я использую его для строк или с нулевыми значениями типы.

string s = someString ?? "Default message"; 

легче, чем

string s; 
    if(someString == null) 
    s = "Default Message";   
    else 
    s = someString; 

или

string s = someString != null ? someString : "Default Message"; 
+2

Очень верно, но это не эквивалентно вашему среднему выражению, как будто someString == string.Empty, the ?? оператор не срабатывает. –

+1

@ The Lame Duck, вы правы. Пустая строка не совпадает с пустой строкой, это просто пример. На самом деле я мог просто заменить строковые проверки нулевыми проверками. Что я должен делать ... – Brandon

+1

См. Также string.IsNullOrEmpty (s). – TrueWill

30

?? Оператор подобен методу coalesce в SQL, он получает вам первое ненулевое значение.

var result = value1 ?? value2 ?? value3 ?? value4 ?? defaultValue; 
+1

Ничего, никогда не думал об использовании его таким образом! – TheSean

+0

Не могу придумать ситуацию используя ?? таким образом, что лучший дизайн не устранит. Хороший вариант использования был бы интересным. – jphofmann

+2

@jphofmann: Один хороший вариант использования из моей текущей работы. Мне нужно искать сложный бизнес-объект, вызывая функцию. Если поиск невозможен, то возвращается функция поиска. Я должен многократно искать различные параметры поиска до тех пор, пока не будет найден объект. Правила поиска очень легко понятны из кода: «var result = Find (« A »)? Найти ("Y") ?? Найти («Q»); 'Использование' ?? 'может сделать ваш код более компактным и читаемым. –

1

В вашем случае вы можете создать DefaultObject как статическое свойство и использовать, как показано ниже ...

var x = (someObject as someType ?? someType.DefaultObject).someMember; 

Где ваш DefaultObject будет возвращать статический, только для чтения объекта.

Как EventArgs.Empty, String.Empty и т.д ...

+0

Для меня это кажется опасным делом. Если я хочу свойство объекта, если оно не является нулевым, этот код приведет к ситуациям, когда я даже не знаю, существует ли объект или нет; возможно, просто происходит совместное использование свойства с «DefaultObject», или, может быть, оно равно null. –

+0

@ Dan, если вам интересно, существует ли объект или нет, тогда проверьте someObject == null. Если вы просто хотите отобразить что-то читаемое пользователю, используйте функцию SomeType.DefaultObject. FYI, этот рефакторинг называется «Ввести нулевой объект». –

0

У меня была точно такая же проблема, как вы, но в пространстве с помощью LinqToXML тег не может быть в документе, поэтому использование ?? ISN 'жизнеспособный для coallescing свойство на этом теге.

Поскольку в C# нет глобальных переменных, все в классе, поэтому я нахожу, как вы, что ?? в основном бесполезно.

Даже простое геттер свойство, как:

MyObject MyObj() { get { return _myObj ?? new MyObj(); } } 

, вероятно, лучше, обслуживаемого инициализаторе ...

Я полагаю, что, как и в SQL было бы в некоторой степени полезными в запросах Linq, но я могу На самом деле, в данный момент это невозможно.

+0

default (MyObj)? что это значит? он будет в любом случае нулевым? –

+0

ха-ха, да, ты прав ... – Pickle

2

Наиболее полезным является использование типов с нулевым значением.

bool? whatabool; 
//... 
bool realBool = whatabool ?? false; 

без ?? вам нужно будет написать следующее, что гораздо более запутанно.

bool realbool = false; 
if (whatabool.HasValue) 
    realbool = whatabool.Value; 

Я нахожу, что этот оператор очень полезен для типов с нулевым значением, но кроме этого я не очень сильно его использую. Определенно крутой ярлык.

+0

Компактный способ представления этого без? есть: bool realBool = (whatabool == null)? false: whatabool; –

1

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

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

string GetStringRep(object key) { 
    object o = container.Get(key) ?? "null"; 
    return o.ToString(); 
} 

ясно, что эти две последовательности эквивалентны:

foo = bar != null ? bar : baz; 
foo = bar ?? baz; 

Второй является просто более лаконична.

+0

Собственно, это не 100% правда. Тройной оператор генерирует другую последовательность IL от оператора нулевой коалесценции. Первый выглядит примерно такие: ldloc.0 brfalse.s isnull1 ldloc.0 уш notnull1 isnull1: ldstr "По умолчанию" notnull1: stloc.1 , а последний, как выглядит такая: ldloc.0 DUP brtrue.s notnull2 поп ldstr «по умолчанию» notnull2: stloc.1 Исходя из этого, я бы сказал, что нулевой оператор коалесцирующий оптимизирован для своей цели и не только чисто синтаксический сахар. –

+0

И я обязательно извиняюсь за форматирование snafu. Тьфу. Я отправлю его в качестве ответа, чтобы выглядеть немного чище. –

+0

Хотя ИЛ отличается, семантика остается эквивалентной. – captncraig

1

Часто кажется хорошей идеей заменить подробный бит синтаксиса более компактной формой. Но обычно это заканчивается тем, что просто запутывает код, не наживая ничего. Современные стандарты защищают более длинные и более описательные имена переменных и используют более подробные стили раскладки, чтобы сделать код более читаемым & более удобным для обслуживания.

Если я собираюсь тренировать свой мозг, чтобы быстро разобрать ??, мне нужно найти ситуацию, когда он показывает явное преимущество - то, что нельзя сделать по-другому, или где-то, что оно экономит значительную сумму печатания.

И это где? падает для меня. Это полезно так редко и?: И if/else - такие быстрые и понятные альтернативы, что на самом деле мало смысла использовать другой синтаксис, который не достигает ничего нового.

a = b ?? c ?? d ?? е; звучит здорово. Но мне никогда не приходилось это делать!

Что я нахожу раздражающим, так это то, что я хочу использовать?, Но каждый раз, когда я думаю «о, возможно, я могу использовать здесь», я понимаю, что я не хочу использовать сам объект, но его членом.

имя = (пользователь == null)? «doe»: user.Surname;

И, к сожалению, ?? здесь не помогает.

+0

P.S. Я не разрабатываю веб-страницы или запросы базы данных LINQ, хотя, может быть, возможно? просто нацелен на другой стиль программирования ... –

11

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

public MyObject MyProperty 
{ 
    get 
    { 
     return this.myField ?? (this.myField = new MyObject()); 
    } 
} 

Это имеет преимущество в том, что в C# присваивание является выражением, так что вы можете назначить и использовать результат выражения (присвоенное значение) все в том же заявлении. Я все еще чувствую себя немного грязным по поводу этого кода, но он более терпим, чем альтернатива (ниже), поэтому он просто выигрывает.

public MyObject MyProperty 
{ 
    get 
    { 
     if (this.myField == null) 
     { 
      this.myField = new MyObject(); 
     } 

     return this.myField; 
    } 
} 
+1

Nice. Дополнительные очки, если вы также можете сделать это потокобезопасным, сохраняя его на одной линии :-) –

7

Как и FYI, тернарный оператор генерирует другую последовательность IL от оператора нулевой коалесценции. Первый выглядит примерно такие:

// string s = null; 
// string y = s != null ? s : "Default"; 
    ldloc.0 
    brtrue.s notnull1 
    ldstr "Default" 
    br.s isnull1 
    notnull1: ldloc.0 
    isnull1: stloc.1 

, а последний, как выглядит, например:

// y = s ?? "Default"; 
    ldloc.0 
    dup 
    brtrue.s notnull2 
    pop 
    ldstr "Default" 
    notnull2: stloc.1 

Исходя из этого, я бы сказал, нулевой оператор коалесцирующий оптимизирован для своей цели и не просто синтаксический сахар.

0

Я часто использую оператор с нулевым коалесцированием при разборе строк в других типах данных. У меня есть набор методов расширения, которые обертывают такие вещи, как Int32.TryParse, и возвращают nullable int вместо того, чтобы подвергать выходной параметр остальной части моего кода. Затем я могу проанализировать строку и предоставить значение по умолчанию, если синтаксический анализ не удался, в одной строке кода.

// ToNullableInt(), is an extension method on string that returns null 
// if the input is null and returns null if Int32.TryParse fails. 

int startPage = Request.QueryString["start"].ToNullableInt() ?? 0; 
3

Я использую его с App.config вещи

string somethingsweet = ConfigurationManager.AppSettings["somesetting"] ?? "sugar"; 
Смежные вопросы