?? เชื่อมต่อกับสตริงว่างหรือไม่


165

สิ่งที่ฉันคิดว่าตัวเองทำมากขึ้นคือการตรวจสอบสตริงว่าง ๆ (เหมือนใน ""หรือเป็นโมฆะ) และโอเปอเรเตอร์ที่มีเงื่อนไข

ตัวอย่างปัจจุบัน:

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

นี่เป็นเพียงวิธีส่วนขยายซึ่งเทียบเท่ากับ:

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

เนื่องจากว่างเปล่าและไม่ว่างจึง??ไม่ทำเคล็ดลับ string.IsNullOrEmpty()รุ่นของ??จะเป็นโซลูชั่นที่สมบูรณ์แบบ ฉันคิดว่าจะต้องมีวิธีที่สะอาดกว่าในการทำสิ่งนี้ (ฉันหวังว่า!) แต่ฉันได้สูญเสียที่จะหามัน

ไม่มีใครรู้วิธีที่ดีกว่าในการทำเช่นนี้แม้ว่าจะเป็นเพียงใน. Net 4.0?


11
เพียงแค่ยั่วเย้าคุณเพียงเล็กน้อยคุณสามารถกำหนดตัวดำเนินการแบบไบนารี ad-hoc แบบกำหนดเอง (และ unary สำหรับเรื่องนั้น) ใน F # ได้อย่างง่ายดาย ที่นี่และใช้มันเหมือนlet (|?) x y = if String.IsNullOrEmpty(x) then y else x s.SiteNumber |? "No Number"
Stephen Swensen

คำตอบ:


72

ไม่มีวิธีในการทำเช่นนี้ คุณสามารถทำให้วิธีการขยายของคุณคืนสตริงหรือโมฆะซึ่งจะช่วยให้ผู้ประกอบการรวมตัวกันในการทำงาน สิ่งนี้จะแปลก แต่ฉันชอบวิธีการของคุณในปัจจุบัน

เนื่องจากคุณใช้วิธีการขยายอยู่แล้วทำไมไม่ลองสร้างวิธีที่ส่งคืนค่าหรือค่าเริ่มต้น:

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

7
ฉันคิดว่าคุณพูดถูกและนี่เป็นคำตอบที่สะอาดที่สุดในขณะนี้ที่ยังอ่านได้ ฉันชอบบางสิ่งที่เหมือน???โอเปอเรเตอร์ใน C # 5 ใครจะรู้
Nick Craver

2
และสิ่งที่จะ ??? ผู้ประกอบการทำอย่างไร ใช้ค่าเริ่มต้นนอกเหนือไปจากค่า null หรือไม่ ฟังดูซับซ้อนที่สุดดีที่สุด
bevacqua

1
ด้วยการแสดงออกแลมบ์ดาอาจจะ? ตัวอย่างเช่นสมมติว่า "item" เป็นโมฆะดังนั้น ... item ?? x=> x.ToString() : null;
Isaac Llopis

@IsaacLlopis ที่ลงท้ายมองยุ่งเหยิงกว่า snippet ดั้งเดิมของ 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;
    }
}

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

นั่นเป็นงานที่ดีงาม
Mariano Luis Villa

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"
llaughlin

อะไร@คำนำหน้าใน@defaultพารามิเตอร์ทำอย่างไร ฉันไม่เคยเห็นแบบนั้นมาก่อน
Drew Chapin

3
@druciferre - ที่ให้คุณใช้defaultเป็นชื่อตัวแปรแม้ว่าจะเป็นคำหลักที่สงวนไว้ใน C #
จัสตินมอร์แกน

1
@ Jimmyt1988 - เพราะใกล้เคียงกับฟังก์ชันT-SQL COALESCEมาตรฐาน
Justin Morgan

1
@ Jimmyt1988 - เพราะมันเลือกฟังก์ชั่นแรกที่ไม่ว่างเปล่าในรายการความยาวโดยพลการ มันเป็นรายละเอียดเล็ก ๆ น้อย ๆ แต่ฟังก์ชั่น T-SQL ทำงานในลักษณะเดียวกัน ชื่อนี้ทำให้ทุกคนที่รู้จักฟังก์ชันนั้นใช้งานง่ายมีหรือไม่มีเอกสาร
Justin Morgan

6

วิธีการขยายที่เร็วกว่าเล็กน้อยที่เสนอก่อนหน้านี้อาจจะ:

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

11
ทำไมไม่ใช้IsNullOrWhitespaceแทนการตัดแต่งและขยายความยาว
weston

1
codeสตริงคงที่สาธารณะรวมตัวกัน (สตริงนี้ @this, สตริง @default = "") {return (@this == null || String.IsNullOrWhiteSpace (@ นี้)? @default: @this; }
paul-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;
}

หรือคืนค่าว่างถ้าสตริงว่างเปล่า

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

ไม่ได้ลองวิธีแก้ปัญหาเหล่านี้


2
ฉันชอบตัวเลือก # 1 ที่นั่นถึงแม้ว่าฉันจะเรียกมันว่า semantic มากกว่าเช่น Or () ดังนั้นฉันจึงสามารถเขียน "string s = s.SiteNumber.Or (" Default ");"
jvenema

2
บางสิ่งบางอย่าง Calling ...OrDefault()จะทำให้เกิดความสับสนถ้ามันไม่ได้ทำตัวเหมือนส่วนที่เหลือของกรอบที่...OrDefault()วิธีการ ชอบหรือไม่นั้น MS ได้ให้ความหมายเฉพาะกับการตั้งชื่อและการเบี่ยงเบนจากพฤติกรรมดังกล่าวในวิธีการที่กำหนดเองซึ่งสร้างความสับสนให้กับผู้ใช้ API ของคุณโดยไม่จำเป็น
mattmc3

3

ฉันกำลังใช้วิธีการขยายสตริงรวมตัวของตัวเอง เนื่องจากผู้ใช้ที่นี่ใช้ 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ทำให้อาร์เรย์ได้รับการจัดสรรโดยไม่จำเป็นเมื่อคุณมีอินพุตเริ่มต้นหนึ่งหรือสองเท่านั้น หลังจากนั้นก็สมเหตุสมผล
Nicholas Petersen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.