?? Menyatu untuk string kosong?

165

Sesuatu yang saya temukan semakin sering dilakukan adalah memeriksa string untuk kosong (seperti dalam ""atau nol) dan operator bersyarat.

Contoh saat ini:

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

Ini hanya metode ekstensi, itu setara dengan:

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

Karena ini kosong dan bukan nol, ??tidak akan melakukan trik. Sebuah string.IsNullOrEmpty()versi ??akan menjadi solusi sempurna. Saya pikir harus ada cara yang lebih bersih untuk melakukan ini (saya harap!), Tetapi saya bingung untuk menemukannya.

Adakah yang tahu cara yang lebih baik untuk melakukan ini, bahkan jika itu hanya di. Net 4.0?

Nick Craver
sumber
11
Hanya untuk sedikit menggiurkan Anda, Anda dapat dengan mudah mendefinisikan operator biner khusus, ad-hoc (dan unary, dalam hal ini) dalam F #. Di sini let (|?) x y = if String.IsNullOrEmpty(x) then y else xdan menggunakannya seperti s.SiteNumber |? "No Number".
Stephen Swensen

Jawaban:

72

Tidak ada cara bawaan untuk melakukan ini. Anda dapat membuat metode ekstensi Anda mengembalikan string atau null, yang akan memungkinkan operator penggabungan untuk bekerja. Namun, ini akan aneh, dan saya pribadi lebih suka pendekatan Anda saat ini.

Karena Anda sudah menggunakan metode ekstensi, mengapa tidak membuatnya saja yang mengembalikan nilai atau default:

string result = s.SiteNumber.ConvertNullOrEmptyTo("No Number");
Reed Copsey
sumber
7
Saya pikir Anda benar, dan ini adalah solusi terbersih yang tersedia saat ini yang masih dapat dibaca. Saya suka sesuatu seperti ???operator di C # 5, siapa tahu.
Nick Craver
2
dan apa yang akan ??? operator lakukan? mengambil nilai default selain nol? terdengar sangat rumit di terbaik
bevacqua
1
Mungkin dengan ekspresi lambda? Misalnya: anggap "item" tidak dapat dibatalkan, maka ... item ?? x=> x.ToString() : null;
Isaac Llopis
@IsaacLlopis yang tampak berantakan daripada cuplikan asli OP
maksymiuk
133

C # sudah memungkinkan kita mengganti nilai nulldengan ??. Jadi yang kita butuhkan adalah ekstensi yang mengubah string kosong menjadi null, dan kemudian kita menggunakannya seperti ini:

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

Kelas ekstensi:

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
sumber
44

Saya tahu ini adalah pertanyaan lama - tetapi saya sedang mencari jawaban dan tidak ada yang sesuai dengan kebutuhan saya dan juga apa yang akhirnya saya gunakan:

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

Pemakaian:

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

EDIT: Cara penulisan fungsi ini yang lebih ringkas adalah:

static string Coalesce(params string[] strings) => strings.FirstOrDefault(s => !string.IsNullOrEmpty(s));
sfsr
sumber
1
Saya suka, tetapi harus memperbaiki kesalahan kompiler dan membuatnya sedikit lebih kompak.
gregmac
Mengapa Anda menyebut Coalesce ini ketika tidak menyatukan nilai, tetapi hanya memilih yang tidak kosong? Itu dude nama yang membingungkan.
Jimmyt1988
8
Karena Coalesce adalah istilah yang digunakan oleh banyak basis data untuk melakukan operasi yang sama (temukan nilai pertama yang bukan nol). Menggabungkan string bersama adalah penggabungan.
Penyerang Cameron
Jawaban terbaik jika Andausing System.Linq
JoePC
Itu elegan, pekerjaan yang bagus.
Mariano Luis Villa
16

Saya memiliki beberapa ekstensi utilitas yang ingin saya gunakan:

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

Sunting: Terinspirasi oleh jawaban sfsr , saya akan menambahkan varian ini ke kotak alat saya mulai sekarang:

public static string Coalesce(this string str, params string[] strings)
{
    return (new[] {str})
        .Concat(strings)
        .FirstOrDefault(s => !string.IsNullOrEmpty(s));
}
Justin Morgan
sumber
1
Saya pasti menggunakan istilah "Penggabungan", karena lebih mirip dengan maksud operator penggabungan nol (??), meskipun saya mengubahnya menjadi "Penggabungan".
llaughlin
Apa yang dilakukan @awalan pada @defaultparameter? Saya belum pernah melihat itu sebelumnya.
Drew Chapin
3
@druciferre - Itu hanya memungkinkan Anda untuk digunakan defaultsebagai nama variabel meskipun kata kunci tersebut disimpan dalam C #.
Justin Morgan
1
@ Jimmyt1988 - Karena mendekati fungsi T-SQL COALESCE standar .
Justin Morgan
1
@ Jimmyt1988 - Juga karena secara khusus memilih fungsi non-kosong pertama dalam daftar panjang sewenang-wenang. Ini detail yang halus, tetapi fungsi T-SQL bekerja dengan cara yang sama. Nama membuatnya intuitif bagi siapa saja yang mengetahui fungsi itu, dengan atau tanpa dokumentasi.
Justin Morgan
6

Metode ekstensi yang sedikit lebih cepat daripada yang diusulkan sebelumnya mungkin:

public static string Fallback(this string @this, string @default = "")
{
    return (@this == null || @this.Trim().Length == 0) ? @default : @this;
}
Chilidog
sumber
11
Mengapa tidak memanfaatkan IsNullOrWhitespace daripada memotong dan memanjangnya.
weston
1
codepublic static string Coalesce (string ini @ini, string @default = "") {return (@ini == null || String.IsNullOrWhiteSpace (@ini))? @ kesalahan: @ ini; }
paul-2011
6

Salah satu keuntungan dari operator null-coalescing adalah hubung singkat. Ketika bagian pertama bukan nol, bagian kedua tidak dievaluasi. Ini bisa bermanfaat ketika fallback membutuhkan operasi yang mahal.

Saya berakhir dengan:

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

Pemakaian:

string navigationTitle = model?.NavigationTitle.
    Coalesce(() => RemoteTitleLookup(model?.ID)). // Expensive!
    Coalesce(() => model?.DisplayName);
Wensveen
sumber
6

Saya hanya menggunakan metode ekstensi NullIfEmpty yang akan selalu mengembalikan nol jika string kosong memungkinkan ?? (Null Coalescing Operator) untuk digunakan seperti biasa.

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

Ini kemudian memungkinkan ?? untuk digunakan seperti biasa dan membuat rantai mudah dibaca.

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

bagaimana dengan metode ekstensi string ValueOrDefault ()

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

atau mengembalikan null jika string kosong:

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

Namun tidak mencoba solusi ini.

devio
sumber
2
Saya suka opsi # 1 di sana, meskipun saya akan menyebutnya sesuatu yang lebih semantik seperti Or (), jadi saya bisa menulis "string s = s.SiteNumber.Or (" Default ");"
jvenema
2
Memanggil sesuatu ...OrDefault()akan membingungkan jika tidak berperilaku seperti ...OrDefault()metode kerangka lainnya . Suka atau tidak suka, MS telah memberi arti khusus pada penamaan itu dan menyimpang dari perilaku itu dalam metode khusus tidak perlu membingungkan bagi pengguna API Anda.
mattmc3
3

Saya menggunakan metode ekstensi string Coalesce saya sendiri. Karena yang ada di sini menggunakan LINQ, dan membuang-buang sumber daya absolutelly untuk operasi intensif waktu (saya menggunakannya dalam putaran yang ketat), saya akan membagikan milik saya:

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

Saya pikir ini cukup sederhana, dan Anda bahkan tidak perlu repot dengan nilai string nol. Gunakan seperti ini:

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

Saya sering menggunakannya. Salah satu fungsi "utilitas" yang Anda tidak dapat hidup tanpanya setelah pertama kali menggunakannya :-)

Loudenvier
sumber
Saya tidak berpikir Anda mengerti mengapa mereka menggunakan LINQ, dan karena parameter dievaluasi sebelum panggilan, Anda s2.Coalesce(s3)dijalankan bahkan ketika tidak diperlukan juga. Lebih baik menggunakan NullIfEmpty()ekstensi dan ??.
NetMage
@ NetMage Saya bisa menjamin Anda bahwa versi LINQ jauh lebih sedikit performanya daripada yang saya sajikan. Anda dapat membuat tolok ukur sederhana untuk menguji ini jika mau. Saya sarankan menggunakan github.com/dotnet/BenchmarkDotNet untuk mencegah perangkap umum saat menulis kode benchmarking.
Loudenvier
0

Saya suka keringkasan metode ekstensi berikut QQQuntuk ini, meskipun tentu saja operator suka? akan lebih baik. Tetapi kita dapat memperbaikinya dengan memperbolehkan tidak hanya dua tetapi tiga nilai opsi string untuk dibandingkan, yang kita temui harus ditangani sesekali (lihat fungsi kedua di bawah).

#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
Nicholas Petersen
sumber
Jika Anda menyukai nama pendek, Anda dapat menyebutnya Or, dan saya akan menggunakan paramskata kunci, seperti jawaban lainnya, yang menghindari definisi ganda untuk beberapa parameter.
Chiel ten Brinke
Terima kasih untuk idenya. Saya sudah lama mengganti nama ini dengan "FirstNotNull" dalam penggunaan saya sendiri. Aktif params, akan lebih baik untuk tidak melakukannya untuk skenario default atau dua, karena paramsmenyebabkan array tidak perlu dialokasikan ketika Anda hanya memiliki satu atau dua input default. Setelah itu masuk akal.
Nicholas Petersen