2017-11-10 2 views
5

私は期待していなかったボクシングと参照の比較について、VB.NETでの動作を今日遭遇しました。説明するために、私はあらゆるタイプの変数を原子的に更新しようとする簡単なプログラムを書いた。オブジェクトボクシングの違い/ C#とVB.Net間の参照の比較

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); 
    } 

} 

このプログラムの出力は次のとおりです:ここで

は、C#でプログラム( https://dotnetfiddle.net/VsMBrg)である

Hello World 
True 
True 
True 
False 
True 
True 

この期待とすべてが正常に動作するようですようです。ここで

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 

最後の文は、セットが機能しなかったことを意味している偽である はここVB.NET(https://dotnetfiddle.net/lasxT2)で同じプログラムです。 ここで何か間違っているのですか、VB.NETの問題ですか?

(注:これは、スレッドに影響されないように、この例では、何のスレッドを持っていない、揮発性がリード/ライト無視)


編集: 私は整数にTを変更する場合は、すべてがOKに動作します
(dotnetfiddle.net/X6uLZs)。 私はカスタムクラスにTを変更した場合も、それはまた、OK作品:
dotnetfiddle.net/LnOOme

答えて

4

私は、この問題の原因は、実際にいくつかの場所では、それはC# 'sのより似てVBのObject取り扱い、あると信じてdynamicは平文よりもObjectです。具体的には、私があれば再書き込みTrySetValueとして:私たちは、Console.WriteLineFalseを印刷することを期待することはありませんでしょう

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 

。しかし、それはまさにそれがしていることです。

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)); 
} 

ああ親愛なる:逆コンパイルバック(リフレクターを使用して)C#にこのコードは、私はこのコードを取得します。ここでGetObjectValueに電話するのは何ですか?さて、の効果は、ボックス化された値のタイプのコピーを作成することになります。curValObjには、_valueと同じオブジェクトへの実際の参照が含まれないため、Interlocked.CompareExchangeは処理できません。実際のオブジェクト参照。

私はこのコードを書き直して、あなたが望むことをする良い方法を考えることはできません。値型で、このオーバーロードを使用しないでください

:そして、おそらく我々はCompareExchangeObject過負荷が私たちに警告する理由さらに理由を見ることができます。

+0

ILを見ると、cb命令は 'unbox.any'で、vb命令は' Conversionsです。ToGenericParameter '。つまり、VBは構造体のコピーを返していますが、C#は同じ構造体を参照していることを意味していますか? – Crowcoder

+0

@Crowcoder - この小さなコードサンプルでは、​​予期しないコピーのせん断数によって、VBコードがどのように動作するかが質問されました:-( –

+0

CLRソースコードから: "技術的には、ボクシングされたDateTimesとDecimalsを" –

関連する問題