Cách tối ưu để sử dụng các toán tử điều kiện null trong các biểu thức boolean


11

Bạn đang viết một biểu thức boolean có thể trông như thế này:

team.Category == "A Team" && team?.Manager?.IsVietnamVet

public class Manager
{
    public bool IsVietnamVet { get; set; }
}

public class Team
{
    public string Category { get; set; }

    public Manager Manager { get; set; }
}

... và bạn gặp lỗi:

Toán tử '&&' không thể được áp dụng cho toán hạng loại 'bool' và 'bool?'

Cách tối ưu / sạch nhất để xử lý nó là gì?

  1. team.Category == "A Team" && (team?.Manager?.IsVietnamVet ?? false)

    Điều đó thực sự có thể đọc được?

  2. team.Category == "A Team" && (team?.Manager?.IsVietnamVet).GetValueOrDefault()

    Nó có thể không hoạt động trong LINQ-to-Entities ...

  3. team.Category == "A Team" && team?.Manager?.IsVietnamVet == true

    Bạn sẽ thực sự viết if (condition == true)mà không có bất kỳ do dự?

Có sự lựa chọn nào khác không? Cuối cùng là tốt hơn để viết:

  1. team.Category == "A Team" && team.Manager != null && team.Manager.IsVietnamVet

if (! (String.IsNullOrEmpty (team.Manager) && condition)) ...
Snoop

1
@StevieV nó đã được đề xuất như thế này ngay từ đầu;)
Santhos

1
Nhìn vào mã cẩn thận hơn, phần có điều kiện null nên team.Manager?.IsVietnamVet, tức là không có điều kiện null sau team, vì đã không thể null.
Svick

2
"Bạn có thực sự viết nếu (điều kiện == đúng) mà không do dự?" Đối với một boolean bình thường, không, vì nó là thừa. Tuy nhiên, đối với một boolean nullable , điều này có liên quan. nullableBool == truevề cơ bản là thử nghiệm cho nullableBool != false && nullableBool != null(và đây là phần thứ hai làm cho nó hữu ích và do đó không thừa)
Flater

2
Tôi bắt đầu sử dụng nullableBool == truenếu tôi không tạo ra một trình bao bọc. Nó có thể đọc được vì bạn thường không viết == truekhi sử dụng boolean thông thường như @Flater đề cập, vì vậy nó gợi ý rằng biến này là null. Thông thường, nó cải thiện khả năng đọc LINQ vì bạn không sử dụng nhiều điều kiện null như @Fabio đề cập.
Santhos

Câu trả lời:


4

Trong trường hợp cụ thể này, có thể là khôn ngoan khi tuân theo Luật Demeter tức là

public class Team
{
    public bool IsManagerVietnamVet => Manager?.IsVietnamVet ?? false;
}    

Tổng quát hơn, nếu một biểu thức boolean phức tạp hoặc xấu thì không có gì để nói bạn không thể chia nó (hoặc một phần của nó) thành một tuyên bố riêng biệt:

bool isVietnamVet = Manager?.IsVietnamVet ?? false;

if (team.Category == "A Team" && isVietnamVet)

Khi bước qua mã trong trình gỡ lỗi, thường sẽ có các điều kiện phức tạp được đóng gói thành một boolchỉ để tiết kiệm một chút di chuột qua di chuột; trong thực tế, nó có thể tốt hơn để đặt toàn bộ vào một boolbiến.

bool isVietnamVetAndCategoryA = (team.Category == "A Team"
    && Manager?.IsVietnamVet ?? false);

if (isVietnamVetAndCategoryA)

hoặc với LINQ:

var wibble = from flight in airport
             from passenger in flight.manifest
             let isOnPlane = 
                 (flight.FinishedBoarding && passenger.Flight == flight.FlightNumber)
             where !isOnPlane
             select passenger;

Tôi nghĩ rằng tôi chủ yếu nghiêng về một giải pháp như vậy.
Santhos

1
Cú pháp C # mới để lưu một vài dòng: công khai bool IsManagerVietnamVet => (team.C Category == "") && (team.Manager? .IsVietnamVet ?? false);
Graham

Chỉ cần nhớ rằng lâu dài a && b && c &&...được thực hiện tuần tự, và cái tiếp theo thậm chí không được thực hiện nếu cái trước đó là false. Vì vậy mọi người thường đặt nhanh nhất vào đầu. Hãy ghi nhớ điều đó khi di chuyển công cụ vào một bool-var
jitbit

@jitbit Đó là thêm về khả năng bảo trì. Tối ưu hóa vi mô như những gì bạn đề cập nói chung là không cần thiết - không đáng lo ngại trừ khi hiệu suất được xác định là một vấn đề và hồ sơ cho thấy thực hiện các thay đổi như thế sẽ có tác động đáng chú ý.
Ben Cottrell

@BenCottrell Tôi sẽ gọi nó là tối ưu hóa "vi mô" nếu IsManagerVietnamVettài sản đi kiểm tra cơ sở dữ liệu;)
jitbit

3

Tôi nghĩ rằng tùy chọn 3 (nghĩa là == true) là cách sạch nhất để kiểm tra đó bool?true, bởi vì nó rất rõ ràng về những gì nó làm.

Trong hầu hết các mã x == truekhông có ý nghĩa, bởi vì nó giống như x, nhưng điều đó không áp dụng ở đây, vì vậy tôi nghĩ == truesẽ không khó hiểu.


1
Tôi nghĩ rằng đây chắc chắn sẽ là con đường để đi nếu chúng ta muốn sử dụng toán tử có điều kiện null, bởi vì nó cũng an toàn linq. Câu hỏi là liệu khả năng đọc là tốt. Theo quan điểm của tôi, sự lựa chọn là giữa opt 3 và opt 4 và tôi sẽ chọn 4 trên 3 vì lý do dễ đọc, ý định của lập trình viên có vẻ rõ ràng hơn một chút. Tôi đã đánh dấu câu trả lời của Ben Cottrell là chính xác vì tôi thích cách tiếp cận đó hơn một chút. Nếu tôi có thể đánh dấu hai câu trả lời, tôi sẽ.
Santhos

1
@Santhos - lại "ý định của lập trình viên có vẻ rõ ràng hơn một chút". IMHO, đây là trường hợp lần đầu tiên lập trình viên thấy a?.b == truehọ sẽ bối rối, nhưng một khi họ nắm bắt được nó đang làm gì, nó trở thành một thành ngữ rất dễ đọc, đẹp hơn nhiều so với việc phải kiểm tra != nullở giữa một biểu thức phức tạp. Tương tự như vậy a?.b ?? false, dường như đã đạt được lực kéo là giải pháp phổ biến nhất, bởi vì nó phù hợp với những gì bạn sẽ gõ nếu bạn đang xử lý một loại khác ngoài boolean [mặc dù tôi không thích nó cho boolean; đối với tôi, nó không đọc tự nhiên; Tôi thích == true].
ToolmakerSteve

3

Mở rộng dựa trên câu trả lời của Ben Cottrell, mẫu "Đối tượng Null" có thể giúp bạn tiếp tục.

Thay vì trả về một nullnhóm / người quản lý, hãy trích xuất ITeamIManagergiao diện và trả về các triển khai thay thế có ý nghĩa:

public class NoManager : IManager
{
    public bool IsVietnamVet => false;
}

public class NoTeam : ITeam
{
    public bool ManagedByVietnamVet => false;

    public IManager Manager => new NoManager();
}

Sau đó, đột nhiên bạn có thể làm team.ManagedByVietnamVetmột cách an toàn.

Tất nhiên điều này phụ thuộc vào nhà cung cấp thượng nguồn teamlà không an toàn - nhưng điều đó có thể được đảm bảo bằng thử nghiệm thích hợp.


-4

Tôi đã viết một lớp đơn giản mà bạn có thể sử dụng:

 public class MyBool 
    {
        public bool? Value { get; set; }

        public MyBool(bool b)
        {
            Value = b;
        }

        public MyBool(bool? b)
        {
            Value = b;
        }

        public static implicit operator bool(MyBool m)
        {
            return m?.Value ?? false;
        }

        public static implicit operator bool?(MyBool m)
        {
            return m?.Value;
        }

        public static implicit operator MyBool(bool m)
        {
            return new MyBool(m);
        }

        public static implicit operator MyBool(bool? m)
        {
            return new MyBool(m);
        }

        public override string ToString()
        {
            return Value.ToString();
        }
    }

Nếu đáng tin cậy để sử dụng một loại tùy chỉnh, tất nhiên. Bạn có thể so sánh MyBoolvới cả hai boolNullable<bool>.


1
Trang web này dành cho các câu hỏi về thiết kế phần mềm thay vì làm thế nào để các đoạn mã cụ thể hoạt động, vì vậy nếu bạn tin rằng đây là giải pháp tốt nhất thì câu trả lời của bạn nên tập trung vào lý do tại sao bạn tin rằng một lớp như thế này là giải pháp tốt nhất có thể, chứ không phải mã chính xác cần thiết để thực hiện nó.
Ixrec
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.