Đánh dấu các tham số là NOT nullable trong C # /. NET?


98

Có thuộc tính hoặc hợp đồng dữ liệu đơn giản nào mà tôi có thể gán cho một tham số hàm ngăn nullviệc truyền trong C # /. NET không? Lý tưởng nhất là điều này cũng sẽ kiểm tra tại thời điểm biên dịch để đảm bảo rằng nghĩa đen nullkhông được sử dụng ở bất kỳ đâu cho nó và tại thời điểm chạy ArgumentNullException.

Hiện tại tôi viết một cái gì đó như ...

if (null == arg)
  throw new ArgumentNullException("arg");

... cho mọi lập luận mà tôi mong đợi là không null.

Trên cùng một lưu ý, có điều ngược lại với Nullable<>điều sau sẽ không thành công:

NonNullable<string> s = null; // throw some kind of exception

7
Với các phiên bản gần đây của C #, việc sử dụng "nameof ()" hoạt động tốt hơn: ném ArgumentNullException mới (nameof (arg)); Bằng cách đó, nếu bạn cấu trúc lại tên, nếu gợn sóng vào tuyên bố ném của bạn
Flydog57

C # 8 hiện hỗ trợ các kiểu tham chiếu nullable, đây là một tùy chọn trình biên dịch mà bạn có thể bật trong dự án của mình. docs.microsoft.com/en-us/dotnet/csharp/nullable-references
Paul Stegler

Câu trả lời:


69

Rất tiếc, không có gì sẵn có tại thời điểm biên dịch.

Tôi có một chút giải pháp hacky mà tôi đã đăng trên blog của mình gần đây, sử dụng cấu trúc và chuyển đổi mới.

Trong .NET 4.0 với các công cụ Code Contracts , cuộc sống sẽ đẹp hơn rất nhiều. Sẽ vẫn khá tốt nếu có cú pháp ngôn ngữ thực tế và hỗ trợ xung quanh tính không null, nhưng các hợp đồng mã sẽ giúp ích rất nhiều.

Tôi cũng có một phương thức mở rộng trong MiscUtil được gọi là ThrowIfNull giúp nó đơn giản hơn một chút.

Một điểm cuối cùng - bất kỳ lý do nào để sử dụng " if (null == arg)" thay vì " if (arg == null)"? Tôi thấy cái sau dễ đọc hơn và vấn đề mà cái trước giải được trong C không áp dụng cho C #.


2
> Rất tiếc, không có gì sẵn có tại thời điểm biên dịch. > ...> Vẫn sẽ khá tốt nếu có cú pháp ngôn ngữ thực tế và hỗ trợ xung quanh tính không null, tôi đồng ý. Tôi cũng muốn thấy lỗi thời gian biên dịch được đưa ra.
AndrewJacksonZA

2
@Jon, "if (arg = null)" không hoạt động nếu tình cờ có một phép ép kiểu ngầm đối với bool được xác định? Tôi thừa nhận nó có vẻ ngoan cố, nhưng nó biên dịch ...
Thomas S. Trias

11
@ ThomasS.Trias: Vâng, trong trường hợp cạnh cực kỳ khó hiểu đó, kết hợp với lỗi đánh máy, kết hợp với việc thiếu các bài kiểm tra xung quanh mã đó, bạn sẽ gặp sự cố. Vào thời điểm đó tôi nghĩ bạn có vấn đề lớn hơn mặc dù :)
Jon Skeet

4
@Jon: Được. Tôi đoán tôi sẽ từ bỏ các điều kiện Yoda yêu quý của tôi. :-)
Thomas S. Trias

1
@SebastianMach: if (null == x)Mặc dù vậy, tôi không tin lý do là do sự khác biệt ngôn ngữ tự nhiên. Tôi tin rằng nó thực sự về một phong cách lỗi thời mà chỉ truyền qua ví dụ, vv
Jon Skeet

21

Tôi biết mình đã trễ kinh nghiệm với câu hỏi này, nhưng tôi cảm thấy câu trả lời sẽ trở nên phù hợp khi phiên bản chính mới nhất của C # đến gần hơn để phát hành, sau đó được phát hành. Trong C # 8.0, một thay đổi lớn sẽ xảy ra, C # sẽ cho rằng tất cả các kiểu được coi là không rỗng.

Theo Mads Torgersen:

Vấn đề là tham chiếu null rất hữu ích. Trong C #, chúng là giá trị mặc định của mọi loại tham chiếu. Giá trị mặc định khác sẽ là gì? Giá trị nào khác mà một biến sẽ có, cho đến khi bạn có thể quyết định điều gì khác để gán cho nó? Giá trị nào khác mà chúng ta có thể mở rộng một mảng tham chiếu mới được phân bổ, cho đến khi bạn bắt đầu điền vào nó?

Ngoài ra, đôi khi null là một giá trị hợp lý trong và của chính nó. Đôi khi bạn muốn trình bày một thực tế rằng, một trường không có giá trị. Rằng việc chuyển “nothing” cho một tham số là ok. Tuy nhiên, sự nhấn mạnh là đôi khi. Và ở đây nằm ở một phần khác của vấn đề: Các ngôn ngữ như C # không cho phép bạn diễn đạt liệu giá trị rỗng ở đây có phải là một ý tưởng tốt hay không.

Vì vậy, giải pháp được đưa ra bởi Mads, là:

  1. Chúng tôi tin rằng thông thường hơn là muốn một tham chiếu không bị rỗng. Loại tham chiếu rỗng sẽ là loại hiếm hơn (mặc dù chúng tôi không có dữ liệu tốt để cho chúng tôi biết bao nhiêu), vì vậy chúng là những loại cần phải có chú thích mới.

  2. Ngôn ngữ này đã có khái niệm về - và cú pháp cho - các kiểu giá trị nullable. Sự tương đồng giữa cả hai sẽ làm cho việc bổ sung ngôn ngữ dễ dàng hơn về mặt khái niệm và đơn giản hơn về mặt ngôn ngữ.

  3. Có vẻ như bạn không nên tạo gánh nặng cho bản thân hoặc người tiêu dùng của mình bằng các giá trị rỗng, trừ khi bạn đã chủ động quyết định rằng bạn muốn chúng. Nulls, không phải sự vắng mặt của chúng, phải là thứ mà bạn rõ ràng phải chọn tham gia.

Ví dụ về tính năng mong muốn:

public class Person
{
     public string Name { get; set; } // Not Null
     public string? Address { get; set; } // May be Null
}

Bản xem trước có sẵn cho bản xem trước Visual Studio 2017, 15.5.4+.


Có ai biết nếu điều này đã bao giờ trở thành một phần của C # 8.0?
TroySteven

@TroySteven Có, bạn phải chọn tham gia thông qua cài đặt trong Visual Studio.
Greg

@TroySteven Đây là tài liệu về nó. docs.microsoft.com/en-us/dotnet/csharp/nullable-references
Greg

Tốt, vì vậy có vẻ như bạn phải kích hoạt nó trước khi nó bắt đầu hoạt động, điều đó rất tốt cho khả năng tương thích ngược.
TroySteven

15

Tôi biết đây là một câu hỏi RẤT cũ, nhưng câu này bị thiếu ở đây:

Nếu bạn sử dụng ReSharper / Rider, bạn có thể sử dụng Khung chú thích .

Chỉnh sửa : Tôi chỉ nhận được một -1 ngẫu nhiên cho câu trả lời này. Tốt rồi. Chỉ cần lưu ý rằng nó vẫn hợp lệ, mặc dù nó không phải là cách tiếp cận được khuyến nghị cho các dự án C # 8.0 + nữa (để hiểu tại sao, hãy xem câu trả lời của Greg ).


1
Điều thú vị, mà theo mặc định ứng dụng biên soạn của bạn sẽ không có tham chiếu đến JetBrains.Annotations.dll vì vậy bạn không cần phải phân phối nó với các ứng dụng: Làm thế nào để sử dụng JetBrains Chú thích để cải thiện thanh tra ReSharper
Lu55

9

Kiểm tra trình xác thực trong thư viện doanh nghiệp. Bạn có thể làm điều gì đó như:

private MyType _someVariable = TenantType.None;
[NotNullValidator(MessageTemplate = "Some Variable can not be empty")]
public MyType SomeVariable {
    get {
        return _someVariable;
    }
    set {
        _someVariable = value;
    }
}

Sau đó, trong mã của bạn khi bạn muốn xác thực nó:

Microsoft.Practices.EnterpriseLibrary.Validation.Validator myValidator = ValidationFactory.CreateValidator<MyClass>();

ValidationResults vrInfo = InternalValidator.Validate(myObject);

0

không phải là đẹp nhất nhưng:

public static bool ContainsNullParameters(object[] methodParams)
{
     return (from o in methodParams where o == null).Count() > 0;
}

bạn cũng có thể sáng tạo hơn trong phương thức ContainsNullParameters:

public static bool ContainsNullParameters(Dictionary<string, object> methodParams, out ArgumentNullException containsNullParameters)
       {
            var nullParams = from o in methodParams
                             where o.Value == null
                             select o;

            bool paramsNull = nullParams.Count() > 0;


            if (paramsNull)
            {
                StringBuilder sb = new StringBuilder();
                foreach (var param in nullParams)
                    sb.Append(param.Key + " is null. ");

                containsNullParameters = new ArgumentNullException(sb.ToString());
            }
            else
                containsNullParameters = null;

            return paramsNull;
        }

tất nhiên bạn có thể sử dụng thiết bị đánh chặn hoặc phản xạ nhưng chúng dễ theo dõi / sử dụng với ít chi phí


3
Rất thực tế xấu sử dụng các truy vấn như: return (from o in methodParams where o == null).Count() > 0; Sử dụng: return methodParams.Any(o=>o==null);nó sẽ nhanh hơn nhiều ở các bộ sưu tập lớn
Yavanosta

Tại sao lại bỏ qua ngoại lệ? Tại sao không ném nó đi?
Martin Capodici

-4

Ok, câu trả lời này hơi muộn, nhưng đây là cách tôi giải quyết nó:

public static string Default(this string x)
{
    return x ?? "";
}

Sử dụng phương thức giải thích này sau đó bạn có thể coi chuỗi rỗng và chuỗi rỗng giống nhau.

Ví dụ

if (model.Day.Default() == "")
{
    //.. Do something to handle no Day ..
}

Tôi biết là không lý tưởng khi bạn phải nhớ gọi mặc định ở mọi nơi nhưng đó là một giải pháp.


5
Làm thế nào là điều này bất kỳ tốt hơn / dễ dàng hơn so với kiểm tra (x == null)? hoặc hàm String.IsNotNullOrEmpty.
Batavia

Không tốt hơn nhiều so với string.IsNotNullOrEmptythực sự khác ngoài đường có thứ bạn quan tâm ở bên trái. Có thể được đưa vào các chức năng khác (độ dài, nối) vv Tốt hơn một chút.
Martin Capodici

@MartinCapodici Ngoài ra, điều này tạo ra ảo tưởng rằng bạn có thể an toàn khi gọi phương thức instance ( Default()) trên null ( model.Day). Tôi biết các phương thức mở rộng không được kiểm tra so với null, nhưng mắt tôi không nhận ra và họ đã tưởng tượng ?ra mã: model?.Day?.Default():)
Alb
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.