?? Объединить для пустой строки?

165

Что-то, что я делаю все больше и больше, проверяет строку на пустое (как в ""или ноль) и условный оператор.

Текущий пример:

s.SiteNumber.IsNullOrEmpty() ? "No Number" : s.SiteNumber;

Это просто метод расширения, он эквивалентен:

string.IsNullOrEmpty(s.SiteNumber) ? "No Number" : s.SiteNumber;

Так как он пустой и не нулевой, ??не сработает. string.IsNullOrEmpty()Версия ??будет идеальным решением. Я думаю, что должен быть более чистый способ сделать это (я надеюсь!), Но я не мог найти его.

Кто-нибудь знает лучший способ сделать это, даже если это только в .Net 4.0?

Ник Крейвер
источник
11
Просто для того, чтобы немного вас соблазнить, вы можете легко определить пользовательские, специальные двоичные (и в этом случае унарные) операторы в F #. Вот let (|?) x y = if String.IsNullOrEmpty(x) then y else xи пользуйся как s.SiteNumber |? "No Number".
Стивен Свенсен

Ответы:

72

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

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

string result = s.SiteNumber.ConvertNullOrEmptyTo("No Number");
Рид Копси
источник
7
Я думаю, что вы правы, и это самое чистое из доступных на данный момент решение, которое все еще читаемо. Я хотел бы что-то вроде ???оператора в C # 5, хотя, кто знает.
Ник Крейвер
2
а что бы то ??? оператор делает? принимать значения по умолчанию в дополнение к нулям? звучит крайне сложно в лучшем случае
беваква
1
Может быть, с лямбда-выражениями? Например: предположим, что «элемент» обнуляем, а затем ... item ?? x=> x.ToString() : null;
Исаак Льопис
@IsaacLlopis, который в конечном итоге выглядит грязнее, чем оригинальный фрагмент OP
maksymiuk
133

C # уже позволяет нам подставлять значения nullс помощью ??. Итак, все, что нам нужно, это расширение, в которое преобразуется пустая строка null, а затем мы используем его следующим образом:

s.SiteNumber.NullIfEmpty() ?? "No Number";

Класс расширения:

public static class StringExtensions
{
    public static string NullIfEmpty(this string s)
    {
        return string.IsNullOrEmpty(s) ? null : s;
    }
    public static string NullIfWhiteSpace(this string s)
    {
        return string.IsNullOrWhiteSpace(s) ? null : s;
    }
}
RedFilter
источник
44

Я знаю, что это старый вопрос, но я искал ответ, и ничего из вышеперечисленного не соответствовало моим потребностям, а также тому, что я в итоге использовал:

private static string Coalesce(params string[] strings)
{
    return strings.FirstOrDefault(s => !string.IsNullOrEmpty(s));
}

Использование:

string result = Coalesce(s.SiteNumber, s.AltSiteNumber, "No Number");

РЕДАКТИРОВАТЬ: еще более компактный способ написания этой функции будет:

static string Coalesce(params string[] strings) => strings.FirstOrDefault(s => !string.IsNullOrEmpty(s));
СФСР
источник
1
Мне нравится, но пришлось исправить ошибку компилятора и сделать его немного более компактным.
gregmac
Почему вы называете это Coalesce, когда он не сводит значения вместе, а просто выбирает тот, который не пуст? Это запутанное имя чувак.
Jimmyt1988
8
Потому что Coalesce - это термин, используемый многими базами данных для выполнения одной и той же операции (найдите первое ненулевое значение). Соединение строк - это конкатенация.
Кэмерон Форвард
Лучший ответ, если выusing System.Linq
JoePC
Это элегантно, хорошая работа.
Мариано Луис Вилла
16

У меня есть пара служебных расширений, которые я люблю использовать:

public static string OrDefault(this string str, string @default = default(string))
{
    return string.IsNullOrEmpty(str) ? @default : str;
}

public static object OrDefault(this string str, object @default)
{
    return string.IsNullOrEmpty(str) ? @default : str;
}

Изменить: Вдохновленный ответом sfsr, теперь я буду добавлять этот вариант в свою панель инструментов:

public static string Coalesce(this string str, params string[] strings)
{
    return (new[] {str})
        .Concat(strings)
        .FirstOrDefault(s => !string.IsNullOrEmpty(s));
}
Джастин морган
источник
1
Я определенно использую термин «Coalesce», так как он больше напоминает намерение оператора нулевого слияния (??), хотя я изменил его на «CoalesceTo».
Ллаглин
Что делает @префикс @defaultпараметра? Я никогда не видел этого раньше.
Дрю Чапин
3
@druciferre - это позволяет вам использовать defaultв качестве имени переменной, даже если это зарезервированное ключевое слово в C #.
Джастин Морган
1
@ Jimmyt1988 - потому что он приближается к стандартной функции T-SQL COALESCE .
Джастин Морган
1
@ Jimmyt1988 - также потому, что он специально выбирает первую непустую функцию в списке произвольной длины. Это тонкая деталь, но функция T-SQL работает так же. Имя делает его интуитивно понятным для любого, кто знает эту функцию, с документацией или без нее.
Джастин Морган
6

Возможно, немного более быстрый метод расширения, чем предложенный ранее:

public static string Fallback(this string @this, string @default = "")
{
    return (@this == null || @this.Trim().Length == 0) ? @default : @this;
}
Chilidog
источник
11
Почему бы не использовать IsNullOrWhitespace, а не обрезать и удлинять его.
Уэстон
1
codeоткрытая статическая строка Coalesce (эта строка @this, строка @default = "") {return (@this == null || String.IsNullOrWhiteSpace (@this))? @default: @this; }
Павел-2011
6

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

Я закончил с:

public static string Coalesce(this string s, Func<string> func)
{
    return String.IsNullOrEmpty(s) ? func() : s;
}

Использование:

string navigationTitle = model?.NavigationTitle.
    Coalesce(() => RemoteTitleLookup(model?.ID)). // Expensive!
    Coalesce(() => model?.DisplayName);
wensveen
источник
6

Я просто использую метод расширения NullIfEmpty, который всегда будет возвращать ноль, если строка пуста, что позволяет ?? (Нулевой оператор объединения), который будет использоваться как обычно.

public static string NullIfEmpty(this string s)
{
    return s.IsNullOrEmpty() ? null : s;
}

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

string string1 = string2.NullIfEmpty() ?? string3.NullIfEmpty() ?? string4;
jjr2000
источник
3

как насчет метода расширения строки ValueOrDefault ()

public static string ValueOrDefault(this string s, string sDefault)
{
    if (string.IsNullOrEmpty(s))
        return sDefault;
    return s;
}

или вернуть ноль, если строка пуста:

public static string Value(this string s)
{
    if (string.IsNullOrEmpty(s))
        return null;
    return s;
}

Не пробовал эти решения, хотя.

devio
источник
2
Мне там нравится вариант № 1, хотя я бы назвал его чем-то более семантическим, например, Or (), поэтому я мог бы написать «string s = s.SiteNumber.Or (" Default ");"
jvenema
2
Вызов чего-либо ...OrDefault()может привести к путанице, если он не будет вести себя как остальные ...OrDefault()методы фреймворка . Нравится вам это или нет, но MS придал конкретное значение тому, что присвоение имен и отклонение от этого поведения в пользовательских методах излишне сбивают с толку пользователей вашего API.
mattmc3
3

Я использую собственный метод расширения Coalesce. Поскольку те, кто здесь используют LINQ, и абсолютно бесполезно тратят ресурсы на операции, требующие много времени (я использую их в тесных циклах), я поделюсь своими:

public static class StringCoalesceExtension
{
    public static string Coalesce(this string s1, string s2)
    {
        return string.IsNullOrWhiteSpace(s1) ? s2 : s1;
    }
}

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

string s1 = null;
string s2 = "";
string s3 = "loudenvier";
string s = s1.Coalesce(s2.Coalesce(s3));
Assert.AreEqual("loudenvier", s);

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

Loudenvier
источник
Я не думаю, что вы понимаете, почему они используют LINQ, и, поскольку параметры оцениваются перед вызовами, ваши s2.Coalesce(s3)запускаются, даже если они не нужны. Лучше использовать NullIfEmpty()расширение и ??.
NetMage
@ NetMage Я могу гарантировать вам, что версии LINQ намного менее производительны, чем та, которую я представил. Вы можете создать простой тест, чтобы проверить это, если хотите. Я предлагаю использовать github.com/dotnet/BenchmarkDotNet для предотвращения распространенных ошибок при написании кода тестирования.
Loudenvier
0

Мне нравится краткость следующего метода расширения QQQдля этого, хотя, конечно, оператор, как? было бы лучше. Но мы можем сделать это 1, позволяя сравнивать не только два, а три значения строковых опций, которые сталкиваются с необходимостью обрабатывать время от времени (см. Вторую функцию ниже).

#region QQ

[DebuggerStepThrough]
public static string QQQ(this string str, string value2)
{
    return (str != null && str.Length > 0)
        ? str
        : value2;
}

[DebuggerStepThrough]
public static string QQQ(this string str, string value2, string value3)
{
    return (str != null && str.Length > 0)
        ? str
        : (value2 != null && value2.Length > 0)
            ? value2
            : value3;
}


// Following is only two QQ, just checks null, but allows more than 1 string unlike ?? can do:

[DebuggerStepThrough]
public static string QQ(this string str, string value2, string value3)
{
    return (str != null)
        ? str
        : (value2 != null)
            ? value2
            : value3;
}

#endregion
Николас Петерсен
источник
Если вам нравятся короткие имена, вы можете их назвать Or, и я бы использовал paramsключевое слово, как и другие ответы, что позволяет избежать дублирования определений для нескольких параметров.
Чиль тен
Спасибо за идею. Я давно заменил это имя на «FirstNotNull» в моем собственном использовании. В paramsэтом случае было бы лучше не делать этого для сценария по умолчанию или двух, поскольку это paramsприводит к ненужному выделению массива, когда у вас есть только один или два входа по умолчанию. После этого имеет смысл.
Николас Петерсен