Dal*_*pić 5 .net c# vb.net boxing reference
我今天在VB.NET中遇到了关于拳击和参考比较的行为,这是我没想到的.为了说明我编写了一个简单的程序,它试图以原子方式更新任何类型的变量.
这是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);
}
}
Run Code Online (Sandbox Code Playgroud)
该程序的输出是:
Hello World
True
True
True
False
True
True
Run Code Online (Sandbox Code Playgroud)
这是预期的,一切似乎都很好.这是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
Run Code Online (Sandbox Code Playgroud)
这里的输出是:
Hello World
True
True
True
False
True
False
Run Code Online (Sandbox Code Playgroud)
这里的最后一个语句是false,这意味着该集合不起作用.我在这里做错了什么,或者是VB.NET中的问题?
(注意:忽略易失性读/写,此示例没有线程,因此不受线程影响)
编辑:如果我将T更改为整数,那么一切正常:
(dotnetfiddle.net/X6uLZs).此外,如果我将T更改为自定义类,它也可以正常工作:
dotnetfiddle.net/LnOOme
我相信这个问题的原因实际上是 VB 的Object处理,在某些地方它比普通dynamic的Object. 具体来说,如果我重写TrySetValue为:
Public Function TrySetValue(oldValue As T, newValue As T) As Boolean
Dim curValObj As Object = _value 'Threading.Volatile.Read(_value)
Console.Write(Object.ReferenceEquals(curValObj,_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
Run Code Online (Sandbox Code Playgroud)
我们绝不会期望它Console.WriteLine会被打印出来False。但这正是它的作用。将此代码反编译回 C#(使用 Reflector),我获得以下代码:
public bool TrySetValue(T oldValue, T newValue)
{
object objectValue = RuntimeHelpers.GetObjectValue(this._value);
Console.Write(object.ReferenceEquals(RuntimeHelpers.GetObjectValue(objectValue), RuntimeHelpers.GetObjectValue(this._value)));
if (!object.Equals(Conversions.ToGenericParameter<T>(objectValue), oldValue))
{
return false;
}
object obj3 = newValue;
return object.ReferenceEquals(RuntimeHelpers.GetObjectValue(Interlocked.CompareExchange(ref this._value, RuntimeHelpers.GetObjectValue(obj3), RuntimeHelpers.GetObjectValue(objectValue))), RuntimeHelpers.GetObjectValue(objectValue));
}
Run Code Online (Sandbox Code Playgroud)
哦亲爱的。所有这些电话GetObjectValue在这里做什么?好吧,它们所产生的效果是导致由装箱值类型组成副本,因此curValObj永远不会包含对与引用相同的对象的实际引用_value,因此Interlocked.CompareExchange当我们处理实际对象引用时,它们永远不会起作用。
目前,我想不出重写这段代码来完成你想要的事情的好方法。也许我们可以看到超载警告我们的进一步原因:ObjectCompareExchange
不要将此重载与值类型一起使用。
| 归档时间: |
|
| 查看次数: |
278 次 |
| 最近记录: |