Kiểm tra null sâu, có cách nào tốt hơn?


130

Lưu ý: Câu hỏi này đã được hỏi trước khi sự ra đời của các .?nhà khai thác trong C # 6 / Visual Studio 2015 .

Tất cả chúng ta đã ở đó, chúng ta có một số tài sản sâu như cake.frosting.berries.loader mà chúng ta cần kiểm tra xem nó có rỗng không nên không có ngoại lệ. Cách làm là sử dụng câu lệnh if ngắn mạch

if (cake != null && cake.frosting != null && cake.frosting.berries != null) ...

Điều này không chính xác thanh lịch, và có lẽ nên có một cách dễ dàng hơn để kiểm tra toàn bộ chuỗi và xem liệu nó có chống lại biến null / thuộc tính không.

Có thể sử dụng một số phương pháp mở rộng hoặc nó sẽ là một tính năng ngôn ngữ, hay nó chỉ là một ý tưởng tồi?


3
Tôi đã mong muốn điều đó thường xuyên đủ - nhưng tất cả các ý tưởng tôi đã đưa ra còn tồi tệ hơn vấn đề thực tế.
peterchen

Cảm ơn tất cả các câu trả lời và thú vị để thấy rằng những người khác đã có cùng suy nghĩ. Tôi đã suy nghĩ về cách tôi muốn thấy điều này tự giải quyết và mặc dù các giải pháp của Eric rất hay nhưng tôi nghĩ rằng tôi chỉ đơn giản là viết một cái gì đó như thế này nếu (IsNull (abc)), hoặc nếu (IsNotNull (abc)) nhưng có lẽ đó là theo sở thích của tôi :)
Homde

Khi bạn khởi tạo sương giá, nó có một tính chất của quả mọng, vì vậy tại thời điểm đó trong nhà xây dựng của bạn, bạn có thể nói với frosting rằng bất cứ khi nào nó được tạo ra để tạo ra một quả mọng rỗng (không-null) không? và bất cứ khi nào quả mọng được sửa đổi sương giá sẽ kiểm tra giá trị ????
Doug Chamberlain

Hơi liên quan một chút, một số kỹ thuật ở đây tôi thấy thích hợp hơn cho vấn đề "null nulls sâu" mà tôi đang cố gắng khắc phục. stackoverflow.com/questions/818642/
Mạnh

Câu trả lời:


223

Chúng tôi đã xem xét thêm một hoạt động mới "?." đến ngôn ngữ có ngữ nghĩa mà bạn muốn. (Và nó đã được thêm ngay bây giờ; xem bên dưới.) Đó là, bạn nói

cake?.frosting?.berries?.loader

và trình biên dịch sẽ tạo ra tất cả các kiểm tra ngắn mạch cho bạn.

Nó không tạo ra thanh cho C # 4. Có lẽ đối với phiên bản tương lai giả thuyết của ngôn ngữ.

Cập nhật (2014): Nhà ?.điều hành hiện đang được lên kế hoạch cho phiên bản trình biên dịch Roslyn tiếp theo. Lưu ý rằng vẫn còn một số tranh luận về phân tích cú pháp và ngữ nghĩa chính xác của toán tử.

Cập nhật (tháng 7 năm 2015): Visual Studio 2015 đã được phát hành và xuất xưởng với trình biên dịch C # hỗ trợ các toán tử có điều kiện null ?.?[] .


10
Không có dấu chấm, nó trở nên mơ hồ về mặt cú pháp với toán tử có điều kiện (A? B: C). Chúng tôi cố gắng tránh các cấu trúc từ vựng yêu cầu chúng tôi "nhìn về phía trước" tùy ý xa trong luồng mã thông báo. (Mặc dù, thật không may, đã có các cấu trúc như vậy trong C #; chúng tôi không muốn thêm nữa.)
Eric Lippert

33
@Ian: vấn đề này cực kỳ phổ biến. Đây là một trong những yêu cầu thường xuyên nhất mà chúng tôi nhận được.
Eric Lippert

7
@Ian: Tôi cũng thích sử dụng mẫu đối tượng null khi khả thi để làm điều đó, nhưng hầu hết mọi người không có hứng thú làm việc với các mô hình đối tượng do chính họ thiết kế. Rất nhiều mô hình đối tượng hiện có sử dụng null và vì vậy đó là thế giới chúng ta phải sống cùng.
Eric Lippert

12
@ John: Chúng tôi nhận được yêu cầu tính năng này gần như hoàn toàn từ các lập trình viên giàu kinh nghiệm nhất của chúng tôi. Các MVP yêu cầu điều này mọi lúc . Nhưng tôi hiểu rằng ý kiến ​​khác nhau; nếu bạn muốn đưa ra một gợi ý thiết kế ngôn ngữ mang tính xây dựng bên cạnh những lời chỉ trích của bạn, tôi rất vui lòng xem xét nó.
Eric Lippert

28
@lazyberezovsky: Tôi chưa bao giờ hiểu cái gọi là "luật" của Demeter; trước hết, nó dường như được gọi chính xác hơn là "Gợi ý của Demeter". Và thứ hai, kết quả của việc "chỉ có một thành viên truy cập" vào kết luận logic của nó là "các đối tượng của Chúa" trong đó mọi đối tượng được yêu cầu làm mọi thứ cho mọi khách hàng, thay vì có thể đưa ra các đối tượng biết cách làm những gì khách hàng muốn Tôi thích hoàn toàn trái ngược với định luật demeter: mọi đối tượng đều giải quyết tốt một số vấn đề nhỏ và một trong những giải pháp đó có thể là "đây là một đối tượng khác giải quyết vấn đề của bạn tốt hơn"
Eric Lippert

27

Tôi đã lấy cảm hứng từ câu hỏi này để thử và tìm hiểu làm thế nào loại kiểm tra null sâu này có thể được thực hiện với cú pháp dễ dàng / đẹp hơn bằng cách sử dụng cây biểu thức. Mặc dù tôi đồng ý với các câu trả lời nói rằng nó có thể là một thiết kế tồi nếu bạn thường cần truy cập các trường hợp nằm sâu trong hệ thống phân cấp, tôi cũng nghĩ rằng trong một số trường hợp, chẳng hạn như trình bày dữ liệu, nó có thể rất hữu ích.

Vì vậy, tôi đã tạo ra một phương thức mở rộng, cho phép bạn viết:

var berries = cake.IfNotNull(c => c.Frosting.Berries);

Điều này sẽ trả về Berry nếu không có phần nào của biểu thức là null. Nếu null gặp phải, null được trả về. Mặc dù vậy, có một số cảnh báo, trong phiên bản hiện tại, nó sẽ chỉ hoạt động với quyền truy cập thành viên đơn giản và nó chỉ hoạt động trên .NET Framework 4, vì nó sử dụng phương thức MemberExpression.Update, mới trong v4. Đây là mã cho phương thức mở rộng IfNotNull:

using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace dr.IfNotNullOperator.PoC
{
    public static class ObjectExtensions
    {
        public static TResult IfNotNull<TArg,TResult>(this TArg arg, Expression<Func<TArg,TResult>> expression)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");

            if (ReferenceEquals(arg, null))
                return default(TResult);

            var stack = new Stack<MemberExpression>();
            var expr = expression.Body as MemberExpression;
            while(expr != null)
            {
                stack.Push(expr);
                expr = expr.Expression as MemberExpression;
            } 

            if (stack.Count == 0 || !(stack.Peek().Expression is ParameterExpression))
                throw new ApplicationException(String.Format("The expression '{0}' contains unsupported constructs.",
                                                             expression));

            object a = arg;
            while(stack.Count > 0)
            {
                expr = stack.Pop();
                var p = expr.Expression as ParameterExpression;
                if (p == null)
                {
                    p = Expression.Parameter(a.GetType(), "x");
                    expr = expr.Update(p);
                }
                var lambda = Expression.Lambda(expr, p);
                Delegate t = lambda.Compile();                
                a = t.DynamicInvoke(a);
                if (ReferenceEquals(a, null))
                    return default(TResult);
            }

            return (TResult)a;            
        }
    }
}

Nó hoạt động bằng cách kiểm tra cây biểu thức đại diện cho biểu thức của bạn và đánh giá các phần lần lượt; mỗi lần kiểm tra rằng kết quả không phải là null.

Tôi chắc chắn điều này có thể được mở rộng để các biểu thức khác ngoài MemberExpression được hỗ trợ. Hãy coi đây là mã bằng chứng khái niệm và xin lưu ý rằng sẽ có một hình phạt hiệu suất bằng cách sử dụng nó (có thể sẽ không quan trọng trong nhiều trường hợp, nhưng đừng sử dụng nó trong một vòng lặp chặt chẽ :-))


Tôi ấn tượng với các kỹ năng lambda của bạn :) tuy nhiên cú pháp có vẻ phức tạp hơn một chút so với mong muốn, ít nhất cho kịch bản if-statement
Homde

Thật tuyệt, nhưng nó chạy mã nhiều hơn 100 lần so với nếu .. &&. Nó chỉ đáng giá nếu nó vẫn biên dịch thành if .. &&.
Monstieur

1
Ah và sau đó tôi thấy DynamicInvokeở đó. Tôi tôn trọng tránh điều đó :)
nawfal

24

Tôi đã tìm thấy tiện ích mở rộng này khá hữu ích cho các tình huống lồng nhau sâu.

public static R Coal<T, R>(this T obj, Func<T, R> f)
    where T : class
{
    return obj != null ? f(obj) : default(R);
}

Đó là một ý tưởng tôi lấy từ toán tử hợp nhất null trong C # và T-SQL. Điều tuyệt vời là kiểu trả về luôn là kiểu trả về của thuộc tính bên trong.

Bằng cách đó bạn có thể làm điều này:

var berries = cake.Coal(x => x.frosting).Coal(x => x.berries);

... Hoặc một biến thể nhỏ ở trên:

var berries = cake.Coal(x => x.frosting, x => x.berries);

Đây không phải là cú pháp tốt nhất mà tôi biết, nhưng nó hoạt động.


Tại sao "Than", mà trông cực kỳ đáng sợ. ;) Tuy nhiên, mẫu của bạn sẽ thất bại nếu phủ sương là null. Nên trông giống như vậy: var berry = cake.NullSafe (c => c.Frosting.NullSafe (f => f.Berry));
Robert Giesecke

Ồ, nhưng bạn đang ám chỉ rằng đối số thứ hai không phải là một cuộc gọi tới Than mà dĩ nhiên nó phải như vậy. Nó chỉ là một sự thay đổi thuận tiện. Bộ chọn (x => x.berries) được chuyển đến lệnh gọi Than bên trong phương thức Than có hai đối số.
John Leidegren

Tên hợp nhất hoặc kết hợp được lấy từ T-SQL, đó là nơi đầu tiên tôi có ý tưởng. IfNotNull ngụ ý rằng một cái gì đó diễn ra nếu không phải là null, tuy nhiên đó là gì, không được giải thích bằng lệnh gọi phương thức IfNotNull. Than thực sự là một cái tên kỳ lạ, nhưng đây là một phương pháp kỳ lạ đáng chú ý.
John Leidegren

Tên theo nghĩa đen tốt nhất cho cái này sẽ là một cái gì đó như "Return IfNotNull" hoặc "ReturnOrDefault"
John Leidegren

@flq +1 ... trong dự án của chúng tôi, nó cũng được gọi là IfNotNull :)
Marc Sigrist

16

Bên cạnh việc vi phạm Luật Demeter, như Mehrdad Afshari đã chỉ ra, đối với tôi, bạn cần "kiểm tra null sâu" cho logic quyết định.

Đây thường là trường hợp khi bạn muốn thay thế các đối tượng trống bằng các giá trị mặc định. Trong trường hợp này, bạn nên xem xét triển khai Mẫu đối tượng Null . Nó hoạt động như một điểm thay thế cho một đối tượng thực, cung cấp các giá trị mặc định và các phương thức "không hành động".


không, object-c cho phép gửi tin nhắn đến các đối tượng null và trả về giá trị mặc định phù hợp nếu cần. Không có vấn đề ở đó.
Julian Rudolph

2
Vâng. Đó là điểm. Về cơ bản, bạn sẽ mô phỏng hành vi ObjC với Mẫu đối tượng Null.
Mehrdad Afshari

10

Cập nhật: Bắt đầu với Visual Studio 2015, trình biên dịch C # (phiên bản ngôn ngữ 6) hiện nhận ra ?.toán tử, giúp "kiểm tra null sâu" một cách dễ dàng. Xem câu trả lời này để biết chi tiết.

Ngoài việc thiết kế lại mã của bạn, như câu trả lời đã xóa này được đề xuất, một tùy chọn khác (mặc dù khủng khiếp) sẽ là sử dụng một try…catchkhối để xem liệu có NullReferenceExceptionxảy ra đôi khi trong quá trình tra cứu thuộc tính sâu sắc đó không.

try
{
    var x = cake.frosting.berries.loader;
    ...
}
catch (NullReferenceException ex)
{
    // either one of cake, frosting, or berries was null
    ...
}

Cá nhân tôi sẽ không làm điều này vì những lý do sau:

  • Nó trông không đẹp.
  • Nó sử dụng xử lý ngoại lệ, nhằm vào các tình huống đặc biệt và không phải là điều mà bạn mong đợi sẽ xảy ra thường xuyên trong quá trình hoạt động bình thường.
  • NullReferenceExceptionCó lẽ không bao giờ nên bị bắt một cách rõ ràng. (Xem câu hỏi này .)

Vì vậy, có thể sử dụng một số phương thức mở rộng hay nó sẽ là một tính năng ngôn ngữ, [...]

Đây gần như chắc chắn phải là một tính năng ngôn ngữ (có sẵn trong C # 6 dưới dạng toán tử .??[]toán tử), trừ khi C # đã có đánh giá lười biếng phức tạp hơn, hoặc trừ khi bạn muốn sử dụng phản xạ (có lẽ cũng không phải là một ý tưởng tốt cho lý do hiệu suất và loại an toàn).

Vì không có cách nào đơn giản để chuyển cake.frosting.berries.loaderđến một hàm (nó sẽ được đánh giá và đưa ra một ngoại lệ tham chiếu null), bạn sẽ phải thực hiện một phương thức tra cứu chung theo cách sau: Nó đưa vào một đối tượng và tên của các thuộc tính để tra cứu:

static object LookupProperty( object startingPoint, params string[] lookupChain )
{
    // 1. if 'startingPoint' is null, return null, or throw an exception.
    // 2. recursively look up one property/field after the other from 'lookupChain',
    //    using reflection.
    // 3. if one lookup is not possible, return null, or throw an exception.
    // 3. return the last property/field's value.
}

...

var x = LookupProperty( cake, "frosting", "berries", "loader" );

(Lưu ý: mã được chỉnh sửa.)

Bạn nhanh chóng thấy một số vấn đề với cách tiếp cận như vậy. Đầu tiên, bạn không nhận được bất kỳ loại an toàn nào và có thể có giá trị tài sản thuộc loại đơn giản. Thứ hai, bạn có thể quay lại nullnếu có sự cố xảy ra và bạn sẽ phải kiểm tra điều này trong chức năng gọi điện của mình hoặc bạn ném ngoại lệ và quay lại nơi bạn bắt đầu. Thứ ba, nó có thể chậm. Thứ tư, nó trông xấu hơn so với những gì bạn bắt đầu.

[...], Hay đó chỉ là một ý tưởng tồi?

Tôi sẽ ở lại với:

if (cake != null && cake.frosting != null && ...) ...

hoặc đi với câu trả lời trên của Mehrdad Afshari.


PS: Quay lại khi tôi viết câu trả lời này, rõ ràng tôi đã không xem xét các cây biểu thức cho các hàm lambda; xem ví dụ câu trả lời của @driis để biết giải pháp theo hướng này. Nó cũng dựa trên một loại phản xạ và do đó có thể không thực hiện tốt như một giải pháp đơn giản hơn ( if (… != null & … != null) …), nhưng nó có thể được đánh giá đẹp hơn từ quan điểm cú pháp.


2
Tôi không biết lý do tại sao điều này bị hạ thấp, tôi đã thực hiện một sự tăng cường cho sự cân bằng: Trả lời là chính xác và mang đến một khía cạnh mới (và đề cập rõ ràng về những hạn chế của giải pháp này ...)
MartinStettner

"câu trả lời trên của Mehrdad Afshari" ở đâu?
Marson Mao

1
@MarsonMao: Trong khi đó, câu trả lời đã bị xóa. (Bạn vẫn có thể đọc nó nếu thứ hạng SO của bạn đủ cao.) Cảm ơn bạn đã chỉ ra lỗi của mình: Tôi nên tham khảo các câu trả lời khác bằng cách sử dụng một siêu liên kết, không sử dụng các từ như "xem bên trên" / "xem bên dưới" (vì câu trả lời không xuất hiện theo thứ tự cố định). Tôi đã cập nhật câu trả lời của mình.
stakx - không còn đóng góp vào

5

Mặc dù câu trả lời của Driis rất thú vị, tôi nghĩ rằng đó là một hiệu suất quá đắt. Thay vì biên dịch nhiều đại biểu, tôi muốn biên dịch một lambda trên mỗi đường dẫn thuộc tính, lưu trữ bộ đệm và sau đó khôi phục lại nhiều loại.

NullCoalesce dưới đây thực hiện điều đó, nó trả về một biểu thức lambda mới với các kiểm tra null và trả về mặc định (TResult) trong trường hợp bất kỳ đường dẫn nào là null.

Thí dụ:

NullCoalesce((Process p) => p.StartInfo.FileName)

Sẽ trả lại một biểu thức

(Process p) => (p != null && p.StartInfo != null ? p.StartInfo.FileName : default(string));

Mã số:

    static void Main(string[] args)
    {
        var converted = NullCoalesce((MethodInfo p) => p.DeclaringType.Assembly.Evidence.Locked);
        var converted2 = NullCoalesce((string[] s) => s.Length);
    }

    private static Expression<Func<TSource, TResult>> NullCoalesce<TSource, TResult>(Expression<Func<TSource, TResult>> lambdaExpression)
    {
        var test = GetTest(lambdaExpression.Body);
        if (test != null)
        {
            return Expression.Lambda<Func<TSource, TResult>>(
                Expression.Condition(
                    test,
                    lambdaExpression.Body,
                    Expression.Default(
                        typeof(TResult)
                    )
                ),
                lambdaExpression.Parameters
            );
        }
        return lambdaExpression;
    }

    private static Expression GetTest(Expression expression)
    {
        Expression container;
        switch (expression.NodeType)
        {
            case ExpressionType.ArrayLength:
                container = ((UnaryExpression)expression).Operand;
                break;
            case ExpressionType.MemberAccess:
                if ((container = ((MemberExpression)expression).Expression) == null)
                {
                    return null;
                }
                break;
            default:
                return null;
        }
        var baseTest = GetTest(container);
        if (!container.Type.IsValueType)
        {
            var containerNotNull = Expression.NotEqual(
                container,
                Expression.Default(
                    container.Type
                )
            );
            return (baseTest == null ?
                containerNotNull :
                Expression.AndAlso(
                    baseTest,
                    containerNotNull
                )
            );
        }
        return baseTest;
    }


3

Tôi cũng thường mong muốn một cú pháp đơn giản hơn! Nó trở nên đặc biệt xấu khi bạn có các giá trị trả về phương thức có thể là null, bởi vì sau đó bạn cần các biến phụ (ví dụ cake.frosting.flavors.FirstOrDefault().loader:)

Tuy nhiên, đây là một giải pháp thay thế khá hay mà tôi sử dụng: tạo phương thức trợ giúp Null-Safe-Chain. Tôi nhận ra rằng điều này khá giống với câu trả lời của @ John ở trên (với Coalphương thức mở rộng) nhưng tôi thấy nó đơn giản hơn và ít gõ hơn. Đây là những gì nó trông giống như:

var loader = NullSafe.Chain(cake, c=>c.frosting, f=>f.berries, b=>b.loader);

Đây là cách thực hiện:

public static TResult Chain<TA,TB,TC,TResult>(TA a, Func<TA,TB> b, Func<TB,TC> c, Func<TC,TResult> r) 
where TA:class where TB:class where TC:class {
    if (a == null) return default(TResult);
    var B = b(a);
    if (B == null) return default(TResult);
    var C = c(B);
    if (C == null) return default(TResult);
    return r(C);
}

Tôi cũng đã tạo ra một số tình trạng quá tải (với 2 đến 6 tham số), cũng như quá tải cho phép chuỗi kết thúc với loại giá trị hoặc mặc định. Điều này làm việc thực sự tốt cho tôi!



1

Như được đề xuất trong câu trả lời của John Leidegren , một cách tiếp cận để giải quyết vấn đề này là sử dụng các phương pháp mở rộng và đại biểu. Sử dụng chúng có thể trông giống như thế này:

int? numberOfBerries = cake
    .NullOr(c => c.Frosting)
    .NullOr(f => f.Berries)
    .NullOr(b => b.Count());

Việc triển khai rất lộn xộn vì bạn cần làm cho nó hoạt động với các loại giá trị, loại tham chiếu và loại giá trị nullable. Bạn có thể tìm một việc thực hiện hoàn toàn trong Timwi 's câu trả lời đến một cách thích hợp để kiểm tra giá trị null là gì? .


1

Hoặc bạn có thể sử dụng sự phản chiếu :)

Chức năng phản xạ:

public Object GetPropValue(String name, Object obj)
    {
        foreach (String part in name.Split('.'))
        {
            if (obj == null) { return null; }

            Type type = obj.GetType();
            PropertyInfo info = type.GetProperty(part);
            if (info == null) { return null; }

            obj = info.GetValue(obj, null);
        }
        return obj;
    }

Sử dụng:

object test1 = GetPropValue("PropertyA.PropertyB.PropertyC",obj);

Trường hợp của tôi (trả về DBNull.Value thay vì null trong hàm phản chiếu):

cmd.Parameters.AddWithValue("CustomerContactEmail", GetPropValue("AccountingCustomerParty.Party.Contact.ElectronicMail.Value", eInvoiceType));

1

Hãy thử mã này:

    /// <summary>
    /// check deep property
    /// </summary>
    /// <param name="obj">instance</param>
    /// <param name="property">deep property not include instance name example "A.B.C.D.E"</param>
    /// <returns>if null return true else return false</returns>
    public static bool IsNull(this object obj, string property)
    {
        if (string.IsNullOrEmpty(property) || string.IsNullOrEmpty(property.Trim())) throw new Exception("Parameter : property is empty");
        if (obj != null)
        {
            string[] deep = property.Split('.');
            object instance = obj;
            Type objType = instance.GetType();
            PropertyInfo propertyInfo;
            foreach (string p in deep)
            {
                propertyInfo = objType.GetProperty(p);
                if (propertyInfo == null) throw new Exception("No property : " + p);
                instance = propertyInfo.GetValue(instance, null);
                if (instance != null)
                    objType = instance.GetType();
                else
                    return true;
            }
            return false;
        }
        else
            return true;
    }

0

Tôi đã đăng bài này tối qua và sau đó một người bạn đã chỉ cho tôi câu hỏi này. Hy vọng nó giúp. Sau đó bạn có thể làm một cái gì đó như thế này:

var color = Dis.OrDat<string>(() => cake.frosting.berries.color, "blue");


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace DeepNullCoalescence
{
  public static class Dis
  {
    public static T OrDat<T>(Expression<Func><T>> expr, T dat)
    {
      try
      {
        var func = expr.Compile();
        var result = func.Invoke();
        return result ?? dat; //now we can coalesce
      }
      catch (NullReferenceException)
      {
        return dat;
      }
    }
  }
}

Đọc bài viết đầy đủ ở đây .

Người bạn tương tự cũng đề nghị bạn xem cái này .


3
Tại sao phải bận tâm Expressionnếu bạn chỉ biên dịch và bắt? Chỉ cần sử dụng một Func<T>.
Scott Rippey

0

Tôi đã sửa đổi một chút mã từ đây để làm cho nó hoạt động cho câu hỏi được hỏi:

public static class GetValueOrDefaultExtension
{
    public static TResult GetValueOrDefault<TSource, TResult>(this TSource source, Func<TSource, TResult> selector)
    {
        try { return selector(source); }
        catch { return default(TResult); }
    }
}

Và vâng, đây có lẽ không phải là giải pháp tối ưu do hàm ý hiệu suất thử / bắt nhưng nó hoạt động:>

Sử dụng:

var val = cake.GetValueOrDefault(x => x.frosting.berries.loader);

0

Nơi bạn cần để đạt được điều này, làm điều này:

Sử dụng

Color color = someOrder.ComplexGet(x => x.Customer.LastOrder.Product.Color);

hoặc là

Color color = Complex.Get(() => someOrder.Customer.LastOrder.Product.Color);

Thực hiện lớp trợ giúp

public static class Complex
{
    public static T1 ComplexGet<T1, T2>(this T2 root, Func<T2, T1> func)
    {
        return Get(() => func(root));
    }

    public static T Get<T>(Func<T> func)
    {
        try
        {
            return func();
        }
        catch (Exception)
        {
            return default(T);
        }
    }
}

-3

Tôi thích cách tiếp cận được thực hiện bởi Objective-C:

"Ngôn ngữ Objective-C thực hiện một cách tiếp cận khác cho vấn đề này và không gọi các phương thức trên nil mà thay vào đó trả về nil cho tất cả các yêu cầu như vậy."

if (cake.frosting.berries != null) 
{
    var str = cake.frosting.berries...;
}

1
những gì một ngôn ngữ khác làm (và ý kiến ​​của bạn về nó) gần như hoàn toàn không liên quan đến việc làm cho nó hoạt động trong C #. Nó không giúp bất cứ ai giải quyết vấn đề C # của họ
ADyson
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.