2015-10-01 26 views
11

Tôi có struct này:Strange hành vi hành chuyển đổi

public struct MyValue 
{ 
    public string FirstPart { get; private set; } 
    public string SecondPart { get; private set; } 

    public static implicit operator MyValue(string fromInput) 
    { // first breakpoint here. 
     var parts = fromInput.Split(new[] {'@'}); 
     return new MyValue(parts[0], parts[1]); 
    } 

    public static implicit operator string(MyValue fromInput) 
    { // second breakpoint here. 
     return fromInput.ToString(); 
    } 

    public override string ToString() 
    { 
     return FirstPart + "@" + SecondPart; 
    } 

    public MyValue(string firstPart, string secondPart) : this() 
    { 
     this.FirstPart = firstPart; 
     this.SecondPart = secondPart; 
    } 
} 

Và tôi đã đặt breakpoint như được chỉ ra bởi các ý kiến ​​trên.

Sau đó, tôi làm điều này:

var first = new MyValue("first", "second"); 
if (first == (MyValue) null) throw new InvalidOperationException(); 

Tôi đang quan sát một số hành vi kỳ lạ khi nó đi vào if (first == (MyValue) null): breakpoint thứ hai là hit đối với một số lý do. Tại sao nó cố chuyển đổi MyValue thành chuỗi để so sánh bình đẳng đơn giản? Sau đó, nếu tôi để mã tiếp tục, nó sẽ chạm vào điểm ngắt đầu tiên, và bây giờ tôi tự hỏi tại sao nó lại cố gắng chuyển đổi một chuỗi (giá trị là null mặc dù thực tế là tôi đã đúc một cách rõ ràng null vào một MyValue) vào một số MyValue? Các chuỗi không nên tham gia khi sử dụng câu lệnh như if (first == (MyValue) null), vậy điều gì thực sự xảy ra ở đây?

+0

mà trong số hai toán tử ngầm mà bạn mong đợi được gọi khi bạn cung cấp 'null'? – Igor

+0

Không, bởi vì tôi "không" chuyển đổi bất cứ điều gì. Nhưng dường như tôi. Do đó câu hỏi của tôi. –

+0

Có thể nó có liên quan đến tình trạng quá tải bị mất đối với toán tử '=='. –

Trả lời

12

Nhận xét bận và nhận thấy vấn đề là gì.

Trình biên dịch C# không thể biên dịch (MyStruct) null, nhưng trong trường hợp của bạn.

Điều này xảy ra khi bạn có toán tử ngầm từ loại tham chiếu (trường hợp này là string) trong đó null hoàn toàn hợp lệ.

Tôi nghĩ rằng bạn có thể làm theo bây giờ tại sao nó thực thi theo cách bạn thấy :)

PS: Đây là một ví dụ điển hình tại sao 'lossy' khai thác ngầm không được khuyến khích nói chung.

+2

Chuyển đổi ngầm định là ma quỷ. –

+2

bạn có thể mô tả chi tiết hơn bằng các từ đơn giản hơn –

+0

Vì vậy, nó chỉ chọn toán tử chuyển đổi ngầm (và chỉ trong trường hợp này) mà nó tìm thấy có thể chuyển đổi một kiểu tham chiếu? Ngoài ra, bạn nói "Trình biên dịch C# không thể biên dịch (MyStruct) null, nhưng trong trường hợp của bạn nó". - bạn có thể giải thích lý do tại sao nó không? –

7

Để hoàn @leppies answer, đây là mã gọi (chế độ Release):

public void X() 
{ 
    var first = new MyValue("first", "second"); 
    if (first == (MyValue) null) throw new InvalidOperationException(); 
} 

nào thực sự biên dịch như sau:

public void X() 
{ 
    if (new MyValue("first", "second") == null) 
    { 
     throw new InvalidOperationException(); 
    } 
} 

Và đây là phát ra IL cho cuộc gọi:

// Methods 
.method public hidebysig 
    instance void X() cil managed 
{ 
    // Method begins at RVA 0x20dc 
    // Code size 45 (0x2d) 
    .maxstack 8 

    IL_0000: ldstr "first" 
    IL_0005: ldstr "second" 
    IL_000a: newobj instance void MyValue::.ctor(string, string) 
    IL_000f: call string MyValue::op_Implicit(valuetype MyValue) 
    IL_0014: ldnull 
    IL_0015: call valuetype MyValue MyValue::op_Implicit(string) 
    IL_001a: call string MyValue::op_Implicit(valuetype MyValue) <--- This! 
    IL_001f: call bool [mscorlib]System.String::op_Equality(string, string) 
    IL_0024: brfalse.s IL_002c 
    IL_0026: newobj instance void [mscorlib]System.InvalidOperationException::.ctor() 
    IL_002b: throw 
    IL_002c: ret 
} // end of method C::X 

Như bạn thấy, sau khi bạn tạo phiên bản mới MyValue, hoạt động IL_001a gọi chuyển đổi ẩn thành string, như đó là khả năng duy nhất cho trình biên dịch thực hiện so sánh loại giá trị với null thực sự biên dịch.

+2

Trong C#, đây là: 'if ((string) MyValue mới (" đầu tiên "," thứ hai ") == (chuỗi) (MyValue) ((chuỗi) null)) ném InvalidOperationException mới(); ' –

+0

@LucasTrzesniewski Không chính xác. Anh ta khai báo một 'implicit' cast thành' string', không phải là một câu lệnh rõ ràng. –

+1

Có, tôi chỉ làm mọi thứ rõ ràng để hiển thị những gì đang diễn ra. –

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