2015-09-11 16 views
23

Câu hỏi này đã được đại tu hoàn toàn vì lợi ích của việc giải thích kỹ lưỡng.C# 6.0 Null Tuyên truyền điều hành và phân bổ tài sản

tôi đã nhận thấy những gì dường như là khá hạn chế kém của các nhà điều hành công tác tuyên truyền null trong C# 6.0 ở chỗ bạn không thể gọi bất động sản setters chống lại một đối tượng mà đã được tuyên truyền null (mặc dù bạn có thể gọi bất động sản getters đối với một đối tượng đã được vô hiệu hóa). Như bạn sẽ thấy từ IL được tạo ra (mà tôi đã phản ánh vào C#), không có gì nên hạn chế khả năng gọi trình định vị thuộc tính bằng cách sử dụng tuyên truyền vô hiệu.

Để bắt đầu, tôi đã tạo một lớp đơn giản, với cả hai phương thức Get/Set kiểu Java và thuộc tính có quyền truy cập công cụ getter/setter công cộng.

public class Person 
{ 
    public Person(string name, DateTime birthday) 
    { 
     Name = name; 
    } 

    public string Name { get; set; } 

    public void SetName(string name) 
    { 
     Name = name; 
    } 

    public string GetName() 
    { 
     return Name; 
    } 
} 

Tôi đã kiểm tra khả năng tuyên truyền vô hiệu trong lớp thử nghiệm sau đây.

public class Program 
{ 
    public static void Main(string[] args) 
    { 
     Person person = new Person("Joe Bloggs", DateTime.Parse("01/01/1991")); 

     // This line doesn't work - see documented error below 
     person?.Name = "John Smith"; 

     person?.SetName("John Smith"); 

     string name = person?.Name; 
    } 
} 

Phía bên tay trái của một bài tập phải là một biến, tài sản hoặc indexer.

Tuy nhiên, bạn có thể nhận thấy rằng cách Java đặt tên bằng cách gọi SetName(...) hoạt động và bạn cũng có thể nhận thấy giá trị của thuộc tính được truyền không có tác dụng.

Chúng ta hãy nhìn vào C# được tạo ra từ mã này:

public static void Main(string[] args) 
{ 
    Person person = new Person("Joe Bloggs", DateTime.Parse("01/01/1991")); 
    if (person != null) 
    { 
     person.SetName("John Smith"); 
    } 
    string arg_33_0 = (person != null) ? person.Name : null; 
} 

Chú ý rằng khi sử dụng chống lại các phương pháp SetName, tuyên truyền rỗng biến đổi đến một if tuyên bố thẳng thắn, và rằng khi sử dụng chống lại các Name property getter, một toán tử bậc ba được sử dụng để nhận giá trị Name hoặc null. Một điều tôi đã nhận thấy ở đây là sự khác biệt về hành vi giữa việc sử dụng câu lệnh if và sử dụng toán tử bậc ba: khi sử dụng trình setter, sử dụng câu lệnh if, trong khi sử dụng toán tử ternary thì không.

public static void Main(string[] args) 
{ 
    Person person = null; 

    if (person != null) 
    { 
     person.Name = "John Smith"; 
    } 

    person.Name = (person != null) ? "John Smith" : null; 
} 

Trong ví dụ này tôi đang sử dụng cả một tuyên bố if và các nhà điều hành ternary để kiểm tra xem người là null trước khi cố gắng để gán cho bất động sản Name của nó. tuyên bố if hoạt động như mong đợi; tuyên bố sử dụng toán tử bậc ba thất bại, như mong đợi

Tham chiếu đối tượng không được đặt thành thể hiện của đối tượng.

Theo tôi, giới hạn xuất phát từ khả năng chuyển đổi không thành công của C# 6.0 thành câu lệnh if hoặc biểu thức ba giây. Nếu nó được thiết kế để chỉ sử dụng các câu lệnh if, việc gán thuộc tính sẽ hoạt động thông qua việc truyền null.

Cho đến nay, tôi chưa thấy một đối số thuyết phục nào về lý do tại sao KHÔNG NÊN này có thể, do đó tôi vẫn đang tìm câu trả lời!

+0

Đó không phải là cách nó hoạt động. Toán tử * trả về * một giá trị sau khi kiểm tra null. –

+0

Không có gì sai với toán tử - trong thực tế, nhiều ngôn ngữ có các toán tử tương tự nhau. Nó chỉ đơn giản là không làm những gì bạn nghĩ rằng nó, cũng không phải tên của nó đề nghị này. Trong thực tế, nó sẽ là một vấn đề nếu nó đã hành động theo cách này - các nhà điều hành sẽ cư xử khác nhau tùy thuộc vào việc nó được ở bên trái hoặc bên phải của một nhiệm vụ. –

+0

Tại sao bạn nghĩ rằng 'null tuyên truyền' đã làm với nhiệm vụ? Nó sẽ * rõ ràng hơn các tên tương tự trong các ngôn ngữ khác (ví dụ như an toàn vô giá như được đề xuất cho Java, điều hướng an toàn trong Groovy, gọi an toàn trong Kotlin) mà nó không gán bất cứ thứ gì nhưng trả về (truyền) null nếu áp dụng trên một đối tượng null –

Trả lời

21

Bạn không phải là người duy nhất! SLaks đã nêu điều này là an issue

Tại sao tôi không thể viết mã như thế này?

Process.GetProcessById(2)?.Exited += delegate { };

và sau đó là một thời gian ngắn đóng cửa như "Theo thiết kế"

không?. Nhà điều hành không bao giờ sản xuất một lvalue, do đó, điều này là do thiết kế.

một ai đó nhận xét rằng nó sẽ là tốt cho setters tài sản cũng như xử lý sự kiện

lẽ thêm cũng tính setters vào yêu cầu như:

Object?.Prop = false;

và nó đã được mở lại dưới dạng yêu cầu tính năng cho C# 7.

4

Bạn không thể sử dụng toán tử truyền bá null theo cách này.

Toán tử này cho phép truyền các giá trị rỗng trong khi đánh giá biểu thức. Nó không thể được sử dụng như là mục tiêu của một nhiệm vụ chính xác như lỗi cho thấy.

Bạn cần phải dính vào vùng đồng bằng kiểm tra null cũ:

if (a != null) 
{ 
    a.Value = someValue; 
} 
+1

@ series0ne chỉnh sửa không kiểm tra tham số 'someValue' cho null, nó kiểm tra đối tượng' a'. Các nhà điều hành không làm những gì bạn nghĩ rằng nó làm ở tất cả. –

+2

Tôi đã cập nhật toàn bộ câu hỏi để minh họa sự cố của mình với vấn đề này.Nó không hiểu cách người vận hành hoạt động - tôi biết điều đó; đó là lý do tại sao, nếu C# 6.0 có thể chuyển đổi null thành các dạng khác nhau (một là câu trả lời được đề xuất của bạn), tại sao nó không thể làm điều này để gán thuộc tính, nó hoàn toàn có thể làm được. làm cái này. – series0ne

-2

Hãy thử nó như thế này ...

using System; 

namespace TestCon 
{ 
    class Program 
    { 
     public static void Main() 
    { 

     Person person = null; 
     //Person person = new Person() { Name = "Jack" }; 

     //Using an "if" null check. 
     if (person != null) 
     { 
      Console.WriteLine(person.Name); 
      person.Name = "Jane"; 
      Console.WriteLine(person.Name); 
     } 

     //using a ternary null check. 
     string arg = (person != null) ? person.Name = "John" : arg = null; 
     //Remember the first statment after the "?" is what happens when true. False after the ":". (Just saying "john" is not enough) 
     //Console.WriteLine(person.Name); 

     if (arg == null) 
     { 
      Console.WriteLine("arg is null"); 
     } 

     Console.WriteLine("Press any key to exit."); 
     Console.ReadKey(); 
    } 
} 
public class Person 
{ 
    public string Name { get; set; } 
} 

}

+0

person.Name = (người! = Null)? "John Smith": null; // trong câu lệnh này nếu điều kiện đánh giá là false, Vì vậy, "person = null" cho "person.Name" không tồn tại. – Jayrooi

+0

Person person = new Person() {Tên = null}; // Ví dụ đối tượng phải tồn tại. person.Name = (person.Name! = Null)? "John": null; Console.WriteLine (person.Name); – Jayrooi

+0

Điều này không trả lời câu hỏi tôi sợ; bạn chỉ minh họa các cách để truyền xung quanh null, mà mọi nhà phát triển C# sẽ sử dụng trước khi giới thiệu toán tử truyền bá null – series0ne

Các vấn đề liên quan