Tôi gặp phải một hành vi trong VB.NET hôm nay liên quan đến boxing và so sánh tham chiếu mà tôi không mong đợi. Để minh họa, tôi đã viết một chương trình đơn giản cố gắng cập nhật nguyên tử biến bất kỳ loại nào.Sự khác biệt trong đối tượng boxing/so sánh tài liệu tham khảo giữa C# và VB.Net
Dưới đây là một chương trình trong C# (https://dotnetfiddle.net/VsMBrg):
using System;
public static class Program
{
private static object o3;
public static void Main()
{
Console.WriteLine("Hello World");
Test<DateTimeOffset?> value = new Test<DateTimeOffset?>();
Console.WriteLine(value.Value == null);
DateTimeOffset dt1 = new DateTimeOffset(2017, 1, 1, 1, 1, 1, TimeSpan.Zero);
DateTimeOffset dt2 = new DateTimeOffset(2017, 1, 2, 1, 1, 1, TimeSpan.Zero);
Console.WriteLine(value.TrySetValue(null, dt1));
Console.WriteLine(value.Value == dt1);
// this should fail
Console.WriteLine(value.TrySetValue(null, dt2));
Console.WriteLine(value.Value == dt1);
// this should succeed
Console.WriteLine(value.TrySetValue(dt1, dt2));
}
}
public class Test<T>
{
public T Value {
get { return (T)System.Threading.Volatile.Read(ref _value); }
}
private object _value;
public bool TrySetValue(T oldValue, T newValue)
{
object curValObj = System.Threading.Volatile.Read(ref _value);
if (!object.Equals((T)curValObj, oldValue))
return false;
object newValObj = (object)newValue;
return object.ReferenceEquals(System.Threading.Interlocked.CompareExchange(ref _value, newValObj, curValObj), curValObj);
}
}
Kết quả của chương trình này là:
Hello World
True
True
True
False
True
True
Đây là như mong đợi và mọi thứ dường như làm việc tốt. Dưới đây là chương trình tương tự trong VB.NET (https://dotnetfiddle.net/lasxT2):
Imports System
Public Module Module1
private o3 as object
Public Sub Main()
Console.WriteLine("Hello World")
Dim value As New Test(Of DateTimeOffset?)
Console.WriteLine(value.Value is nothing)
Dim dt1 As New DateTimeOffset(2017, 1, 1, 1, 1, 1, TimeSpan.Zero)
Dim dt2 As New DateTimeOffset(2017, 1, 2, 1, 1, 1, TimeSpan.Zero)
Console.WriteLine(value.TrySetValue(Nothing, dt1))
Console.WriteLine(value.Value = dt1)
' This should fail
Console.WriteLine(value.TrySetValue(Nothing, dt2))
Console.WriteLine(value.Value = dt1)
' This should succeed
Console.WriteLine(value.TrySetValue(dt1, dt2))
End Sub
End Module
public class Test(Of T)
Public readonly Property Value As T
Get
Return CType(Threading.Volatile.Read(_value), T)
End Get
End Property
Private _value As Object
Public Function TrySetValue(oldValue As T, newValue As T) As Boolean
Dim curValObj As Object = Threading.Volatile.Read(_value)
If Not Object.Equals(CType(curValObj, T), oldValue) Then Return False
Dim newValObj = CObj(newValue)
Return Object.ReferenceEquals(Threading.Interlocked.CompareExchange(_value, newValObj, curValObj), curValObj)
End Function
end class
Ở đây, đầu ra là:
Hello World
True
True
True
False
True
False
đây báo cáo kết quả cuối cùng là sai có nghĩa là các thiết lập đã không làm việc. Tôi có làm gì sai ở đây hay là vấn đề trong VB.NET?
(Lưu ý: Bỏ qua dễ bay hơi lần đọc/viết, ví dụ này không có chủ đề để nó không bị ảnh hưởng bởi luồng)
Edit: Nếu tôi thay đổi T để nguyên sau đó mọi thứ hoạt động OK:
(dotnetfiddle.net/X6uLZs). Ngoài ra nếu tôi thay đổi T để một lớp tùy chỉnh nó cũng làm việc OK:
dotnetfiddle.net/LnOOme
Nhìn vào IL, lệnh C# là 'unbox.any' trong đó lệnh vb là' Chuyển đổi.ToGenericParameter '. Vì vậy, điều đó có nghĩa là vb đang trả về một bản sao của cấu trúc nhưng C# đang tham chiếu cùng một cấu trúc, đúng không? –
Crowcoder
@Crowcoder - số lượng cắt các bản sao không mong muốn được tạo ra trong mẫu mã nhỏ này đã khiến tôi đặt câu hỏi về cách mã VB hoạt động :-( –
Từ mã nguồn CLR: "Về mặt kỹ thuật, chúng tôi có thể trả về ngày và giờ thập phân được đóng hộp mà không sao chép chúng ở đây, nhưng VB nhận ra rằng đây sẽ là một thay đổi đột phá cho khách hàng của họ. Vì vậy, hãy sao chép chúng. " –