?? Boş dize için birleşme?


165

Kendimi gittikçe daha fazla yaparken bulduğum bir şey, bir dizeyi boş (içinde ""veya boş) ve koşullu bir işleç için kontrol etmektir .

Mevcut bir örnek:

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

Bu sadece bir uzantı yöntemidir:

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

Boş olduğu ve boş ??olmadığı için hile yapmayacak. Bir string.IsNullOrEmpty()versiyonu ??mükemmel çözüm olacaktır. Bunu yapmanın daha temiz bir yolu olması gerektiğini düşünüyorum (umarım!), Ama onu bulmakta kayboldum.

Herkes sadece .Net 4.0'da olsa bile bunu yapmanın daha iyi bir yolunu biliyor mu?


11
Sadece sizi biraz büyülemek için, F # 'da özel, geçici ikili (ve bu konuda tekli) işleçleri kolayca tanımlayabilirsiniz. İşte let (|?) x y = if String.IsNullOrEmpty(x) then y else xve böyle kullanın s.SiteNumber |? "No Number".
Stephen Swensen

Yanıtlar:


72

Bunu yapmanın yerleşik bir yolu yok. Bununla birlikte, uzantı yönteminizin bir dize veya null döndürmesini sağlayabilirsiniz, bu da birleştirme operatörünün çalışmasına izin verir. Ancak bu garip olurdu ve ben şahsen mevcut yaklaşımınızı tercih ediyorum.

Zaten bir uzantı yöntemi kullandığınızdan, neden yalnızca değeri veya varsayılanı döndüren bir yöntem yapmıyorsunuz:

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

7
Bence haklısın ve bu hala okunabilir olan en temiz çözüm. ???Kim bilir, ben C # 5 bir operatör gibi bir şey isterdim .
Nick Craver

2
ve ne ??? operatör yapmak? null değerlerine ek olarak varsayılan değerleri al? en iyi son derece karmaşık geliyor
bevacqua

1
Lambda ifadeleriyle belki? Örneğin: "item" in geçersiz olduğunu varsayalım, o zaman ... item ?? x=> x.ToString() : null;
Isaac Llopis

@IsaacLlopis OP'nin orijinal snippet'inden daha dağınık görünüyor
maksymiuk

133

C # zaten değerleri nullile değiştirmemizi sağlar ??. Yani ihtiyacımız olan tek şey, boş bir dizeyi dönüştüren bir uzantı nullve bunu şu şekilde kullanıyoruz:

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

Uzantı sınıfı:

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

Bu eski bir soru olduğunu biliyorum - ama bir cevap arıyordum ve yukarıdakilerin hiçbiri benim ihtiyacım yanı sıra kullanarak sona erdi:

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

Kullanımı:

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

EDIT: Bu işlevi yazmanın daha da kompakt bir yolu şöyle olacaktır:

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

1
Beğendim, ancak bir derleyici hatasını düzeltmek zorunda kaldı ve biraz daha kompakt hale getirdi.
gregmac

Neden değerleri bir araya getirmediği, ancak yalnızca boş olmayanı seçtiğinde bu Coalesce'ı çağırıyorsunuz? Kafa karıştırıcı bir isim ahbap.
Jimmyt1988

8
Çünkü Coalesce, birçok veritabanının aynı işlemi yapmak için kullandığı terimdir (ilk null olmayan değeri bulun). Dizeleri birleştirmek birleştirme işlemidir.
Cameron Forward

En iyi cevapusing System.Linq
JoePC

Bu zarif ve güzel bir iş.
Mariano Luis Villa

16

Kullanmak istediğim birkaç yardımcı program uzantısı var:

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

Düzenleme: sfsr'ın cevabından esinlenerek , şu andan itibaren bu varyantı araç kutuma ekleyeceğim:

public static string Coalesce(this string str, params string[] strings)
{
    return (new[] {str})
        .Concat(strings)
        .FirstOrDefault(s => !string.IsNullOrEmpty(s));
}

1
"CoalesceTo" olarak değiştirilmiş olsa da, boş birleştirme operatörünün (??) niyetine daha çok benzediğinden kesinlikle "Coalesce" terimini kullanıyorum.
llaughlin

Parametredeki @önek ne @defaultişe yarar ? Bunu daha önce hiç görmedim.
Drew Chapin

3
@druciferre - Bu default, C # 'da ayrılmış bir anahtar kelime olmasına rağmen değişken adı olarak kullanmanızı sağlar .
Justin Morgan

1
@ Jimmyt1988 - Çünkü standart T-SQL COALESCE fonksiyonuna yaklaşıyor .
Justin Morgan

1
@ Jimmyt1988 - Ayrıca , rastgele uzunluk listesindeki ilk boş olmayan işlevi seçtiği için . İnce bir ayrıntı ama T-SQL işlevi aynı şekilde çalışıyor. İsim, bu işlevi belgeleyen veya içermeyen bilen herkes için sezgisel hale getirir.
Justin Morgan

6

Belki daha önce önerilenden biraz daha hızlı bir genişletme yöntemi:

public static string Fallback(this string @this, string @default = "")
{
    return (@this == null || @this.Trim().Length == 0) ? @default : @this;
}

11

1
codepublic static string Coalesce (bu dize @this, string @default = "") {return (@this == null || String.IsNullOrWhiteSpace (@this))? @ varsayılan: bu; }
paul-2011

6

Sıfır birleştirici operatörün avantajlarından biri, kısa devre yapmasıdır. İlk bölüm boş olmadığında, ikinci bölüm değerlendirilmez. Geri dönüş pahalı bir işlem gerektirdiğinde bu yararlı olabilir.

Sonunda:

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

Kullanımı:

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

6

Ben sadece dize izin veriyorsa boş her zaman null döndürecek bir NullIfEmpty uzatma yöntemi kullanın ?? (Boş Birleştirme Operatörü) normal olarak kullanılmalıdır.

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

Bu daha sonra izin verir ?? normal olarak kullanılmasını sağlar ve zincirin okunmasını kolaylaştırır.

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

3

ValueOrDefault () bir dize uzantısı yöntemi nasıl

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

veya dize Boş ise null değerini döndürür:

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

Yine de bu çözümleri denemedim.


2
Or () gibi daha semantik bir şey çağırmak rağmen seçenek # 1 seviyorum, bu yüzden "string s = s.SiteNumber.Or (" Default ");"
jvenema

2
...OrDefault()Çerçevenin geri kalan ...OrDefault()yöntemleri gibi davranmasaydı, bir şeyi çağırmak kafa karıştırıcı olurdu . Beğen ya da beğenme, MS, özel yöntemlerde bu adlandırma ve bu davranıştan sapmanın API'nizin kullanıcıları için gereksiz yere kafa karıştırıcı olduğuna özel bir anlam vermiştir.
mattmc3

3

Kendi dize Coalesce uzantısı yöntemi kullanıyorum. Burada olanlar LINQ kullanıyor ve zaman yoğun operasyonlar için mutlak kaynak israfı (sıkı döngülerde kullanıyorum), benim paylaşacağım:

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

Bence bu oldukça basit ve null dize değerleri ile uğraşmanıza bile gerek yok. Şöyle kullanın:

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

Onu çok kullanırım. İlk kez kullanmadan yaşayamayacağınız bu "faydalı" işlevlerden biri :-)


Neden LINQ kullandıklarını anladığınızı düşünmüyorum ve parametreler aramalardan önce değerlendirildiğinden, s2.Coalesce(s3)ihtiyacınız olmadığında bile çalıştırılıyor. Bir NullIfEmpty()uzantıyı kullanmak daha iyi ve ??.
NetMage

@NetMage LINQ sürümlerinin sunduğumdan çok daha az performans gösterdiğini garanti edebilirim. İsterseniz bunu test etmek için basit bir karşılaştırma ölçütü oluşturabilirsiniz. Kıyaslama kodu yazarken sık karşılaşılan tuzakları önlemek için github.com/dotnet/BenchmarkDotNet kullanmanızı öneririz .
Loudenvier

0

QQQTabii ki bir operatör olsa da, bunun için aşağıdaki uzatma yönteminin kısaca seviyorum? Daha iyi olurdu. Ancak bunu sadece iki değil üç dize seçenek değerinin karşılaştırılmasına izin vererek 1'e kadar yapabiliriz;

#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

Kısa isimlerden hoşlanıyorsanız, onu arayabilirdiniz ve diğer cevaplar gibi, birden fazla parametre için yinelenen tanımları önleyen anahtar kelimeyi Orkullanırdım params.
Chiel ten Brinke

Fikir için teşekkürler. Ben uzun zamandan beri kendi adıma "FirstNotNull" ile bu adı değiştirdi. Açık olarak params, varsayılan senaryo veya iki için bunu yapmamak daha iyi paramsolur , çünkü yalnızca bir veya iki girişe sahip olduğunuzda bir dizinin gereksiz yere tahsis edilmesine neden olur. Bundan sonra mantıklı.
Nicholas Petersen
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.