?? Coalesce cho chuỗi rỗng?


165

Một cái gì đó tôi thấy mình làm nhiều hơn và nhiều hơn nữa là kiểm tra một chuỗi trống (như trong ""hoặc null) và một toán tử có điều kiện.

Một ví dụ hiện tại:

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

Đây chỉ là một phương thức mở rộng, nó tương đương với:

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

Vì nó trống rỗng và không rỗng, ??sẽ không làm trò bịp. Một string.IsNullOrEmpty()phiên bản ??sẽ là giải pháp hoàn hảo. Tôi nghĩ rằng phải có một cách sạch hơn để làm điều này (tôi hy vọng!), Nhưng tôi đã không thể tìm thấy nó.

Có ai biết một cách tốt hơn để làm điều này, ngay cả khi nó chỉ trong .Net 4.0?


11
Chỉ cần kích thích bạn một chút, bạn có thể dễ dàng xác định các toán tử tùy chỉnh, nhị phân (và đơn nguyên, cho vấn đề đó) trong F #. Ở đây let (|?) x y = if String.IsNullOrEmpty(x) then y else xvà sử dụng nó như thế nào s.SiteNumber |? "No Number".
Stephen Swensen

Câu trả lời:


72

Không có cách tích hợp để làm điều này. Bạn có thể làm cho phương thức tiện ích mở rộng của mình trả về một chuỗi hoặc null, tuy nhiên, điều này sẽ cho phép toán tử kết hợp hoạt động. Điều này sẽ là lạ, tuy nhiên, cá nhân tôi thích cách tiếp cận hiện tại của bạn.

Vì bạn đã sử dụng một phương thức tiện ích mở rộng, tại sao không chỉ tạo một phương thức trả về giá trị hoặc mặc định:

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

7
Tôi nghĩ bạn đã đúng, và đây là giải pháp sạch nhất hiện có mà vẫn có thể đọc được. Tôi muốn một cái gì đó giống như một ???nhà điều hành trong C # 5, ai biết được.
Nick Craver

2
và những gì sẽ ??? Toán tử làm gì? lấy giá trị mặc định ngoài null? nghe có vẻ cực kỳ phức tạp
bevacqua

1
Với biểu thức lambda có lẽ? Ví dụ: giả sử "mục" là không thể, sau đó ... item ?? x=> x.ToString() : null;
Isaac Llopis

@IsaacLlopis cuối cùng trông lộn xộn hơn đoạn trích ban đầu của OP
maksymiuk

133

C # đã cho phép chúng tôi thay thế các giá trị cho nullbằng ??. Vì vậy, tất cả những gì chúng ta cần là một tiện ích mở rộng chuyển đổi một chuỗi rỗng thành nullvà sau đó chúng ta sử dụng nó như thế này:

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

Lớp mở rộng:

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

Tôi biết đây là một câu hỏi cũ - nhưng tôi đang tìm câu trả lời và không có câu hỏi nào ở trên phù hợp với nhu cầu của tôi cũng như những gì tôi đã sử dụng:

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

Sử dụng:

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

EDIT: Một cách thậm chí nhỏ gọn hơn để viết chức năng này sẽ là:

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

1
Tôi thích, nhưng phải sửa một lỗi biên dịch và làm cho nó gọn hơn một chút.
gregmac

Tại sao bạn gọi đây là Coalesce khi nó không mang các giá trị lại với nhau, mà chỉ chọn một giá trị không trống? Đó là một tên khó hiểu anh chàng.
Jimmyt1988

8
Bởi vì Coalesce là thuật ngữ được nhiều cơ sở dữ liệu sử dụng để thực hiện cùng một hoạt động (tìm giá trị không null đầu tiên). Nối các chuỗi với nhau là nối.
Cameron Forward

Câu trả lời hay nhất nếu bạnusing System.Linq
JoePC

Đó là công việc thanh lịch, tốt đẹp.
Biệt thự Mariano Luis

16

Tôi có một vài tiện ích mở rộng tiện ích mà tôi muốn sử dụng:

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

Chỉnh sửa: Lấy cảm hứng từ câu trả lời của sfsr , từ giờ tôi sẽ thêm biến thể này vào hộp công cụ của mình:

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

1
Tôi chắc chắn đang sử dụng thuật ngữ "Coalesce", vì nó gần giống với ý định của toán tử hợp nhất null (??), mặc dù tôi đã đổi nó thành "CoalesceTo".
llaughlin

Không những gì @tiền tố trên @defaultthông số làm gì? Tôi chưa bao giờ thấy điều đó trước đây.
vẽ Chapin

3
@druciferre - Điều đó chỉ cho phép bạn sử dụng defaultlàm tên biến mặc dù đó là một từ khóa dành riêng trong C #.
Justin Morgan

1
@ Jimmyt1988 - Bởi vì nó gần đúng với hàm COALESCE T-SQL tiêu chuẩn .
Justin Morgan

1
@ Jimmyt1988 - Cũng bởi vì nó đặc biệt chọn hàm không trống đầu tiên trong danh sách độ dài tùy ý. Đây là một chi tiết tinh tế, nhưng hàm T-SQL hoạt động theo cùng một cách. Tên làm cho nó trực quan cho bất cứ ai biết chức năng đó, có hoặc không có tài liệu.
Justin Morgan

6

Một phương pháp mở rộng nhanh hơn một chút so với đề xuất trước đó có lẽ:

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

11
Tại sao không sử dụng IsNullOrWhitespace thay vì cắt và kéo dài nó.
weston

1
codechuỗi tĩnh công khai Coalesce (chuỗi này @this, chuỗi @default = "") {return (@this == null | | String.IsNullOrWhiteSpace (@this))? @default: @this; }
paul-2011

6

Một trong những lợi thế của toán tử hợp nhất null là nó ngắn mạch. Khi phần thứ nhất không có giá trị, phần thứ hai không được đánh giá. Điều này có thể hữu ích khi dự phòng đòi hỏi một hoạt động đắt tiền.

Tôi đã kết thúc với:

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

Sử dụng:

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

6

Tôi chỉ đơn giản sử dụng một phương thức mở rộng Null IfEmpty sẽ luôn trả về null nếu chuỗi trống cho phép ?? (Null Coalescing Toán tử) được sử dụng như bình thường.

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

Điều này sau đó cho phép ?? được sử dụng như bình thường và làm cho chuỗi dễ đọc.

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

3

làm thế nào về một phương thức mở rộng chuỗi ValueOrDefault ()

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

hoặc trả về null nếu chuỗi rỗng:

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

Không thử các giải pháp này mặc dù.


2
Tôi thích tùy chọn số 1 ở đó, mặc dù tôi gọi nó là một cái gì đó có ngữ nghĩa hơn như Or (), vì vậy tôi có thể viết "chuỗi s = s.SiteNumber.Or (" Mặc định ");"
jvenema

2
Gọi một cái gì đó ...OrDefault()sẽ gây nhầm lẫn nếu nó không hoạt động như các ...OrDefault()phương thức còn lại của khung . Dù muốn hay không, MS đã đưa ra một ý nghĩa cụ thể cho việc đặt tên và đi lệch khỏi hành vi đó trong các phương thức tùy chỉnh là gây nhầm lẫn không cần thiết cho người dùng API của bạn.
mattmc3

3

Tôi đang sử dụng một phương thức mở rộng chuỗi Coalesce của riêng tôi. Vì những người ở đây đang sử dụng LINQ và lãng phí tài nguyên tuyệt đối cho các hoạt động đòi hỏi nhiều thời gian (tôi đang sử dụng nó trong các vòng lặp chặt chẽ), tôi sẽ chia sẻ của tôi:

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

Tôi nghĩ nó khá đơn giản và thậm chí bạn không cần bận tâm đến các giá trị chuỗi null. Sử dụng nó như thế này:

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

Tôi sử dụng nó rất nhiều. Một trong những chức năng "tiện ích" mà bạn không thể sống sau lần đầu tiên sử dụng :-)


Tôi không nghĩ bạn hiểu lý do tại sao họ sử dụng LINQ và vì các tham số được đánh giá trước các cuộc gọi, nên bạn s2.Coalesce(s3)cũng chạy ngay cả khi không cần thiết. Tốt hơn để sử dụng một NullIfEmpty()phần mở rộng và ??.
NetMage

@NetMage Tôi có thể đảm bảo với bạn rằng các phiên bản LINQ có hiệu suất thấp hơn rất nhiều so với phiên bản tôi đã trình bày. Bạn có thể tạo một điểm chuẩn đơn giản để kiểm tra điều này nếu bạn muốn. Tôi đề nghị sử dụng github.com/dotnet/BenchmarkDotNet để ngăn những cạm bẫy phổ biến khi viết mã điểm chuẩn.
Lâu đài

0

Tôi thích sự ngắn gọn của phương pháp mở rộng sau đây QQQcho điều này, mặc dù tất nhiên là một toán tử như thế nào? sẽ tốt hơn. Nhưng chúng ta có thể so sánh điều này bằng cách cho phép so sánh không chỉ hai mà ba giá trị tùy chọn chuỗi, mà một trong số đó gặp phải nhu cầu xử lý mọi lúc và sau đó (xem hàm thứ hai bên dưới).

#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

Nếu bạn thích tên ngắn, bạn có thể gọi nó Orvà tôi sẽ sử dụng paramstừ khóa, giống như các câu trả lời khác, để tránh các định nghĩa trùng lặp cho nhiều tham số.
Chiel ten Brinke

Cảm ơn ý kiến. Từ lâu, tôi đã thay thế tên này bằng "FirstNotNull" theo cách sử dụng của riêng tôi. Bật params, tốt hơn là không làm điều đó cho kịch bản mặc định hoặc hai, vì paramslàm cho một mảng được phân bổ không cần thiết khi bạn chỉ có một hoặc hai đầu vào mặc định. Sau đó có ý nghĩa.
Nicholas Petersen
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.