it-roy-ru.com

Уникальные способы использования оператора Null Coalescing

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

string nobody = null;
string somebody = "Bob Saget";
string anybody = "";

anybody = nobody   ?? "Mr. T"; // returns Mr. T
anybody = somebody ?? "Mr. T"; // returns "Bob Saget"

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

nobody = null;
anybody = nobody == null ? "Bob Saget" : nobody; // returns Bob Saget

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

  • Вы использовали ?? для чего-то еще?

  • Требуется ли ??, или вы просто должны использовать троичный оператор (с которым .__ больше всего знакомы)

148
Armstrongest

Ну, во-первых, это гораздо проще, чем стандартная троичная:

string anybody = parm1 ?? localDefault ?? globalDefault;

vs.

string anyboby = (parm1 != null) ? parm1 
               : ((localDefault != null) ? localDefault 
               : globalDefault);

Это также хорошо работает, если нулевой объект не является переменной:

string anybody = Parameters["Name"] 
              ?? Settings["Name"] 
              ?? GlobalSetting["Name"];

против 

string anybody = (Parameters["Name"] != null ? Parameters["Name"] 
                 : (Settings["Name"] != null) ? Settings["Name"]
                 :  GlobalSetting["Name"];
197
James Curran

Я использовал его как однорядную ленту загрузки:

public MyClass LazyProp
{
    get { return lazyField ?? (lazyField = new MyClass()); }
}

Удобочитаемый? Решай сам.

172
Cristian Libardo

Я нашел это полезным двумя «немного странными» способами:

  • В качестве альтернативы для наличия параметра out при написании подпрограмм TryParse (т.е. возвращает нулевое значение, если синтаксический анализ не выполняется)
  • Как представление «не знаю» для сравнения

Последнему нужно немного больше информации. Как правило, когда вы создаете сравнение с несколькими элементами, вам нужно посмотреть, дает ли первая часть сравнения (например, возраст) окончательный ответ, а затем следующую часть (например, имя), только если первая часть не помогла. Использование оператора объединения нулей означает, что вы можете писать довольно простые сравнения (будь то для упорядочения или равенства). Например, используя пару вспомогательных классов в MiscUtil

public int Compare(Person p1, Person p2)
{
    return PartialComparer.Compare(p1.Age, p2.Age)
        ?? PartialComparer.Compare(p1.Name, p2.Name)
        ?? PartialComparer.Compare(p1.Salary, p2.Salary)
        ?? 0;
}

По общему признанию у меня теперь есть ProjectionComparer в MiscUtil, вместе с некоторыми расширениями, которые делают такие вещи еще проще - но это все еще изящно.

То же самое можно сделать для проверки на референтное равенство (или нулевое значение) в начале реализации Equals.

51
Jon Skeet

Другое преимущество состоит в том, что троичный оператор требует двойной оценки или временной переменной.

Рассмотрим это, например:

string result = MyMethod() ?? "default value";

в то время как с троичным оператором у вас остается либо:

string result = (MyMethod () != null ? MyMethod () : "default value");

который дважды вызывает MyMethod или:

string methodResult = MyMethod ();
string result = (methodResult != null ? methodResult : "default value");

В любом случае, нулевой оператор объединения более чистый и, я думаю, более эффективный.

32
Mario Cossi

Еще одна вещь, которую следует учитывать, это то, что оператор coalesce не вызывает метод get свойства дважды, как это делает троичный оператор.

Таким образом, есть сценарии, в которых вы не должны использовать троичный, например:

public class A
{
    var count = 0;
    private int? _prop = null;
    public int? Prop
    {
        get 
        {
            ++count;
            return _prop
        }
        set
        {
            _prop = value;
        }
    }
}

Если вы используете: 

var a = new A();
var b = a.Prop == null ? 0 : a.Prop;

метод получения будет вызван дважды, а переменная count будет равна 2, и если вы используете:

var b = a.Prop ?? 0

переменная count будет равна 1, как и должно быть.

20
Fabio Lima

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

int? test = null;
var result = test ?? 0; // result is int, not int?

Я часто использую это в запросах Linq:

Dictionary<int, int?> PurchaseQuantities;
// PurchaseQuantities populated via ASP .NET MVC form.
var totalPurchased = PurchaseQuantities.Sum(kvp => kvp.Value ?? 0);
// totalPurchased is int, not int?
14
Ryan

Я использовал ?? в моей реализации IDataErrorInfo:

public string Error
{
    get
    {
        return this["Name"] ?? this["Address"] ?? this["Phone"];
    }
}

public string this[string columnName]
{
    get { ... }
}

Если какое-либо отдельное свойство находится в состоянии «ошибка», я получаю эту ошибку, в противном случае я получаю ноль. Работает действительно хорошо.

9
Matt Hamilton

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

public void Method(Arg arg = null)
{
    arg = arg ?? Arg.Default;
    ...
7
Niall Connaughton

Является ?? необходимо, или вы просто должны использовать троичный оператор (с которым большинство знакомо)

На самом деле, мой опыт показывает, что слишком немногие знакомы с троичным оператором (или, точнее, с оператором conditional; ?: является «троичным» в том же смысле, что || является двоичным или + является унарным или двоичным; однако, это действительно единственный троичный оператор во многих языках), так что, по крайней мере, в этом ограниченном примере ваше утверждение не выполнится.

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

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

5
a CVn

Мне нравится использовать оператор null coalesce для отложенной загрузки определенных свойств.

Очень простой (и надуманный) пример, чтобы проиллюстрировать мою точку зрения:

public class StackOverflow
{
    private IEnumerable<string> _definitions;
    public IEnumerable<string> Definitions
    {
        get
        {
            return _definitions ?? (
                _definitions = new List<string>
                {
                    "definition 1",
                    "definition 2",
                    "definition 3"
                }
            );
        }
    } 
}
5
mlnyc

Единственная проблема в том, что оператор null-coalesce не обнаруживает пустые строки. 


то есть.

string result1 = string.empty ?? "dead code!";

string result2 = null ?? "coalesced!";

Результат:

result1 = ""

result2 = coalesced!

В настоящее время я смотрю на переопределение ?? Оператор обойти это. Конечно, было бы удобно встроить это в Framework.

Мысли?

4
whatispunk

В последнее время я часто использовал одну вещь - использовать объединение нулей для резервного копирования в as. Например:

object boxed = 4;
int i = (boxed as int?) ?? 99;

Console.WriteLine(i); // Prints 4

Это также полезно для резервного копирования длинных цепочек ?., каждый из которых может дать сбой

int result = MyObj?.Prop?.Foo?.Val ?? 4;
string other = (MyObj?.Prop?.Foo?.Name as string)?.ToLower() ?? "not there";
4
Blue0500

Является ?? необходимо, или вы просто должны использовать троичный оператор (с которым большинство знакомо)

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

С другой стороны, так как это настолько специализировано, я не думаю, что у этого есть другое использование. Я бы предпочел соответствующую перегрузку оператора ||, как это делают другие языки. Это было бы более экономно в дизайне языка. Но хорошо …

3
Konrad Rudolph

Здорово! Считай меня кем-то, кто не знал об операторе слияния null - это довольно изящная штука.

Мне гораздо легче читать, чем троичный оператор.

Первое, что приходит мне в голову, где я могу использовать это - хранить все мои параметры по умолчанию в одном месте.

public void someMethod( object parm2, ArrayList parm3 )
{ 
  someMethod( null, parm2, parm3 );
}
public void someMethod( string parm1, ArrayList parm3 )
{
  someMethod( parm1, null, parm3 );
}
public void someMethod( string parm1, object parm2, )
{
  someMethod( parm1, parm2, null );
}
public void someMethod( string parm1 )
{
  someMethod( parm1, null, null );
}
public void someMethod( object parm2 )
{
  someMethod( null, parm2, null );
}
public void someMethod( ArrayList parm3 )
{
  someMethod( null, null, parm3 );
}
public void someMethod( string parm1, object parm2, ArrayList parm3 )
{
  // Set your default parameters here rather than scattered through the above function overloads
  parm1 = parm1 ?? "Default User Name";
  parm2 = parm2 ?? GetCurrentUserObj();
  parm3 = parm3 ?? DefaultCustomerList;

  // Do the rest of the stuff here
}
3
HanClinto

Немного странный случай использования, но у меня был метод, в котором объект IDisposable потенциально передается как arg (и, следовательно, располагается родителем), но также может иметь значение null (и поэтому должен быть создан и размещен в локальном методе)

Чтобы использовать его, код либо выглядел как

Channel channel;
Authentication authentication;

if (entities == null)
{
    using (entities = Entities.GetEntities())
    {
        channel = entities.GetChannelById(googleShoppingChannelCredential.ChannelId);
        [...]
    }
}
else
{
    channel = entities.GetChannelById(googleShoppingChannelCredential.ChannelId);
    [...]
}

Но с нулевым слиянием становится намного аккуратнее

using (entities ?? Entities.GetEntities())
{
    channel = entities.GetChannelById(googleShoppingChannelCredential.ChannelId);
    [...]
}
1
PaulG

нуль-коалесцирующий оператор

Этот оператор проверяет переменную, если ноль, возвращает значение рядом с оператором «??», иначе возвращает значение, сохраненное в переменной.

Например: -1

var name=”ATHUL”;
var result =name ?? “The name is null”
Console.WriteLine(result);

о/п: ATHUL

Например: -2 

var name=null;
var result =name ?? “The name is null”
Console.WriteLine(result);

о/п: имя ноль

0
Athul Nalupurakkal