ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

对象装箱的差异/比较C#和VB.Net之间的参考

2019-05-22 19:56:48  阅读:290  来源: 互联网

标签:c net reference vb-net boxing


我今天在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);
    }

}

该程序的输出是:

Hello World
True
True
True
False
True
True

这是预期的,一切似乎都很好.
这是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

这里的输出是:

Hello World
True
True
True
False
True
False

这里的最后一个语句是false,这意味着该集合不起作用.
我在这里做错了什么,或者是VB.NET中的问题?

(注意:忽略易失性读/写,此示例没有线程,因此不受线程影响)

编辑:
如果我将T更改为整数,那么一切正常:
(dotnetfiddle.net/X6uLZs).
此外,如果我将T更改为自定义类,它也可以正常工作:
dotnetfiddle.net/LnOOme

解决方法:

我相信这个问题的原因实际上是VB的Object处理,在某些地方它更像C#的动态而不是普通的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

我们永远不会指望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));
}

噢亲爱的.在这里做GetObjectValue的所有电话是什么?好吧,他们所拥有的效果是使副本由盒装值类型组成,因此curValObj永远不会包含对_value引用的同一对象的实际引用,因此当我们处理时,Interlocked.CompareExchange永远不会工作实际对象引用.

我想不出一个好的方法来重写这个代码,目前,做你想要的.也许我们可以看到CompareExchange的Object重载警告我们的另一个原因:

Do not use this overload with value types.

标签:c,net,reference,vb-net,boxing
来源: https://codeday.me/bug/20190522/1153512.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有