Оператор ?? в C# 2.0

(ASP.NET) · English (12,576 views)

Появившийся в .NET 2.0 оператор ?? уверенно занимает в моем хит-параде наиболее часто используемых приемов первое место, но при этом довольно редко используется в проектах, которые я разрабатывал в команде. Поэтому довольно часто встречаются в коде конструкции вроде:

1
2
3
4
5
public string Caption
{
    get { return ViewState["Caption"] != null ? (string) ViewState["Caption"] : ""; }
    set { ViewState["Caption"] = value; }
}

Или даже:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public string VisibleStatistic
{
    get
    {
        string retval = (string) ViewState["VisibleStatistic"];
        return retval == null ? "" : retval;
    }
    set
    {
        ViewState["VisibleStatistic"] = value;
    }
}

public string SelectedCategoryName
{
    get
    {
        object retval = ViewState["SelectedCategoryName"];
        if (retval != null)
            return (string) retval;
        return String.Empty;
    }
    set
    {
        ViewState["SelectedCategoryName"] = value;
    }
}

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

Оператор ?? возвращает левый операнд, если он не равен null, и правый в противном случае.

Как минимум ничего сложного. Давайте перепишем все три примера, используя этот оператор:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public string Caption
{
    get { return (string) ViewState["Caption"] ?? String.Empty; }
    set { ViewState["Caption"] = value; }
}

public string VisibleStatistic
{
    get { return (string) ViewState["VisibleStatistic"] ?? String.Empty; }
    set { ViewState["VisibleStatistic"] = value; }
}

public string SelectedCategoryName
{
    get { return (string) ViewState["SelectedCategoryName"] ?? String.Empty; }
    set { ViewState["SelectedCategoryName"] = value; }
}

В случае с value-типами, такой способ не сработает: мы получим NullReferenceException. Но не расстраивайтесь, в этом случае мы можем воспользоваться Nullable-типами. Было:

1
2
3
4
5
public int FirstWidth
{
    get { return ViewState["FirstWidth"] != null ? (int) ViewState["FirstWidth"] : 0; }
    set { ViewState["FirstWidth"] = value; }
}

Стало:

1
2
3
4
5
public int FirstWidth
{
    get { return (int?) ViewState["FirstWidth"] ?? 0; }
    set { ViewState["FirstWidth"] = value; }
}

В случае использования оператора ?? выражение вычисляется слева направо, поэтому можно использовать конструкции вроде:

1
string name = FirstName ?? LastName ?? "Anonymous";

Естественно, это выглядит намного более читабельно, чем:

1
2
3
4
5
6
7
string name;
if (FirstName != null)
    name = FirstName;
else if (LastName != null)
    name = LastName;
else
    name = "Anonymous";

И уж тем более, чем:

1
2
3
string name = FirstName != null
        ? FirstName
        : (LastName != null ? LastName : "Anonymous");

Берите на вооружение!

18 Responses to this entry

Subscribe to comments with RSS

SHAman @
said on 14.01.2008 at 22.00 · Permalink

Отлично! Спасибо, буду знать. Очень удобно. Особенно строить цепи типа:

1
string name = FirstName ?? LastName ?? "Anonymous";

PS. В форме комментариев два флажка уведомления по мылу. Поставил оба, для верности:)

said on 15.01.2008 at 12.06 · Permalink

Действительно полезный оператор.
В Groovy (увы с .NET я не дружен) он называется “Elvis operator” и обозначается “смайлом” ?:

said on 15.01.2008 at 13.07 · Permalink

В JavaScript сделано проще:

1
var name = firstName || lastName || "Anonymous";
que
said on 15.01.2008 at 13.17 · Permalink

ruby way:

1
first_name || last_name || "Anonymous"
said on 15.01.2008 at 14.56 · Permalink

2lusever: не совсем правильно. Вот в этом случае JavaScript ведет себя не так, как C# и ruby:

1
2
3
var firstName = "", middleName = false, lastName = 0;
// Тут будет "Anonymous", а в C# и ruby - ""
var name = firstName || middleName || lastName || "Anonymous";
said on 15.01.2008 at 18.12 · Permalink

2 lusever

даже не смотря на то, что в JS сделано не совсем то, не ясно в чём “проще”?
‘||’ писать проще чем ‘??’, что-ли? :) анекдот прямо:
- ‘||’ проще чем ‘??’!
- чем проще-то?
- чем ‘??’!

Akela
said on 16.01.2008 at 10.50 · Permalink

Откровенно говоря восхищаться тут нечем

1
return (int?) ViewState["FirstWidth"] ?? 0;

Очевидно, что начальное значение переменной просто неправильно проинициализировано null и это обязательно выплывет где-нибудь в другом не столь очевидном месте.

1
string name = FirstName ?? LastName ?? "Anonymous";

Непонятно, чем это лучше

1
return Array.Find({FirstName, LastName, "Anonimous"}, NotNull);

PS

Прослеживая историю развития С# легко убедиться, что он семимильными шагами движется в сторону Perl, где подобные быстрые операторы уже сыграли злую шутку – зачастую пишутся программы понятные только создателю, да и то не всегда.

said on 16.01.2008 at 11.08 · Permalink

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

Итак, по порядку.

1
return (int?) ViewState["FirstWidth"] ?? 0;

Насчет непроинициализированного значения переменной — тут Вы правы. Но если проинициализировать,– значение 0 попадет во ViewState, что означает увеличение размера страницы. А зачем это нужно, если переменная имеет значение по умолчанию?

1
2
string name = FirstName ?? LastName ?? "Anonymous";
return Array.Find({FirstName, LastName, "Anonimous"}, NotNull);

Непонятно, чем лучше? Тогда почему бы не писать

1
int sum = System.Int32.Add(2, 3);

Ведь это то же самое.

Насчет Вашего P.S. Тут вообще мрак. Перл никогда не был функциональным языком, а именно в этом направлении движется C#.

said on 16.01.2008 at 15.25 · Permalink

C# куда движется?!? ты чего это? imo ты тут очень ошибаешься. напомню, что функциональный язык избегает состояний и изменяемых объектов.

ты может что-то другое хотел сказать?

said on 16.01.2008 at 15.51 · Permalink

Я что хотел сказать, то сказал. Считаю, что C# движется в сторону функциональщины (читай F# [читай Ocaml]).

said on 16.01.2008 at 18.30 · Permalink

ну это ж бред :)

F# такой себе мультипарадигмный язык. C# такой себе императивный ОО язык. движутся параллельными курсами и пересекаются только в IL :)

или ты предполагаешь в (не)далёком будущем смерть(забытие, поглощение) одного из упомянутых языков в пользу другого?

said on 16.01.2008 at 18.45 · Permalink

Нет, конечно, ничьей смерти я не жду :-) Просто как-то издавно повелось, что часть фич, успешно обкатанных на F#, портируют на C#.

А насчет функциональных возможностей – так, навскидку: 1.1 -> 2.0 (анонимные функции, замыкания) -> 3.5 (лямбда-выражения, анонимные типы [кстати, неизменяемые], linq). Я не говорю, что C# станет полностью функциональным (это, естественно, бред). И не говорю, что F# умрет (тем более его не так давно начали продвигать в массы). Я просто говорю, что C# идет ну никак не сторону Perl.

Akela
said on 17.01.2008 at 15.06 · Permalink

Простите, но Ваши представления о “функциональности” несколько некорректные о чем вам правильно заметил COTOHA. Лямбда выражения, анонимные функция, замыкания и прочая упомянутая ерунда не являются основными признаками функционального языка. Основное свойство функциональных языков – отсутствие переменных. Именно поэтому C# более Perl (в нем это все есть), нежели Lisp и уж тем более Haskel.

Более того он вообще не имеет определенной коцепции, что ярко доказываетя ??, using и прочим синтаксическим шумом.

Возможно ?? ддя отдельных маргиналов и полезен, хотя и путает код. Но почему тогда нет определения любого оператора, например !? или +!.

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

GolDen @
said on 05.03.2008 at 12.15 · Permalink

Хочу попросить автора замечательного блога написать свое IMHO по поводу кодо-генерирующий шаблонных фреймворков для .NET, типа CodeSmith, SmartCode, SQLNetFramework. Использует ли автор что-то подобное, если да, то что именно, какое мнение сложилось от других продуктов?

said on 12.07.2008 at 15.07 · Permalink

Уже самый популярный оператор среди тех кто кодит на .NET 2.0, очень удобно и просто применять. У меня он дааавно на вооружении)

said on 07.10.2008 at 0.03 · Permalink

Coalesce ?? operator in C# 2.0 | Dmytro Shteflyuk’s Home…

Появившийся в .NET 2.0 оператор ?? уверенно занимает в моем хит-параде наиболее часто используемых приемов первое место, но при этом довольно …

said on 25.10.2008 at 7.30 · Permalink

По долгу службы мне приходится перелопачивать горы кода на C# и Java. Честно говоря, такого рода операторы в больших количествах делают код малочитаемым. И я бы не рекомендовал им злоупотреблять, особенно в больших проектах.

said on 03.04.2009 at 11.03 · Permalink

Hi,i like this plugin.
And I used it in my site.
But I’m so gloomy,because when i use

1
the_content(__('Read more...', 'inove'))

it was transferred meaning like this

1
var_dump(array($a => "xx"));

the realyy it seemed is

1
var_dump(array($a =&lt"xx"));

how can i do ?

Comments are closed

Comments for this entry are closed for a while. If you have anything to say – use a contact form. Thank you for your patience.