?? Злиття порожнього рядка?


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

Не існує вбудованого способу для цього. Однак ви можете змусити ваш метод розширення повернути рядок або null, що дозволить оператору з’єднання працювати. Однак це було б дивно, і я особисто віддаю перевагу вашому нинішньому підходу.

Оскільки ви вже використовуєте метод розширення, чому б просто не зробити той, який повертає значення або за замовчуванням:

string result = s.SiteNumber.ConvertNullOrEmptyTo("No Number");

7
Я думаю, ти маєш рацію, і це найчистіше рішення, яке зараз доступне для читання. Мені б хотілося щось на зразок ???оператора в C # 5, хоча хто знає.
Нік Крейвер

2
а що б ??? оператор робити? приймати значення за замовчуванням, крім нулів? в кращому випадку звучить надзвичайно складно
bevacqua

1
Можливо, з лямбда-виразами? Наприклад: припустимо, що "предмет" є нульовим, тоді ... item ?? x=> x.ToString() : null;
Ісаак Льопис

@IsaacLlopis, який виглядає безладним, ніж оригінальний фрагмент ОП
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;
    }
}

44

Я знаю, що це давнє запитання, але я шукав відповідь, і жодне з перерахованого вище не відповідало моїй потребі, а також тому, що я в кінцевому підсумку використовував:

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

Використання:

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

EDIT: Ще більш компактним способом написання цієї функції буде:

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;
}

11
Чому б не скористатися IsNullOrWhitespace, а не обрізати та подовжити його.
weston

1
codeзагальнодоступна статична рядок Coalesce (ця рядок @this, рядок @default = "") {return (@this == null || String.IsNullOrWhiteSpace (@this))? @ за замовчуванням: @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);

6

Я просто використовую метод розширення NullIfEmpty, який завжди поверне null, якщо рядок порожній, що дозволяє ?? (Null Coalescing Operator), який слід використовувати як звичайний.

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

Це потім дозволяє ?? використовувати його як звичайне і робить ланцюжок легким для читання.

string string1 = string2.NullIfEmpty() ?? string3.NullIfEmpty() ?? string4;

3

як щодо методу розширення рядка ValueOrDefault ()

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

або повернути null, якщо рядок порожній:

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

Хоча не пробували ці рішення.


2
Мені подобається варіант №1, хоча я б назвав це щось більш семантичне, як Or (), тому я міг би написати "string s = s.SiteNumber.Or (" За замовчуванням ");"
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);

Я цим багато користуюся. Одна з таких функцій "утиліти", без якої ви не можете жити, не використовуючи її :-)


Я не думаю, що ви розумієте, чому вони використовують LINQ, і оскільки параметри оцінюються перед викликами, ваш s2.Coalesce(s3)запуск виконується навіть тоді, коли він не потрібен. Краще використовувати NullIfEmpty()розширення і ??.
NetMage

@NetMage Я можу вам гарантувати, що версії LINQ набагато менш ефективні, ніж представлені нами. Ви можете створити простий орієнтир, щоб перевірити це, якщо хочете. Я пропоную використовувати github.com/dotnet/BenchmarkDotNet для запобігання загальних підводних каменів під час написання коду бенчмаркінгу.
Loudenvier

0

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

#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ключове слово, як і інші відповіді, що дозволяє уникнути дублювання визначень для кількох параметрів.
Chiel ten Brinke

Дякую за ідею. Я давно замінив це ім’я на "FirstNotNull" у власному використанні. Увімкнення params, було б краще не робити цього для сценарію за замовчуванням або двох, оскільки це paramsпризводить до необхідності виділення масиву, коли у вас є лише один або два входи за замовчуванням. Після цього має сенс.
Ніколас Петерсен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.