2010-12-08 4 views
14

私は、TCPサーバーへのクライアント接続を処理するクラスを開始しています。ここで私は、これまでのコード書いたされていますByRef vs ByVal Clarification

Imports System.Net.Sockets 
Imports System.Net 

Public Class Client 
    Private _Socket As Socket 

    Public Property Socket As Socket 
     Get 
      Return _Socket 
     End Get 
     Set(ByVal value As Socket) 
      _Socket = value 
     End Set 
    End Property 

    Public Enum State 
     RequestHeader ''#Waiting for, or in the process of receiving, the request header 
     ResponseHeader ''#Sending the response header 
     Stream ''#Setup is complete, sending regular stream 
    End Enum 

    Public Sub New() 

    End Sub 

    Public Sub New(ByRef Socket As Socket) 
     Me._Socket = Socket 

    End Sub 
End Class 

だから、私のオーバーロードされたコンストラクタで、私ははい、System.Net.Sockets.Socketインスタンスから参照を受け入れるのですか?

私のSocketプロパティでは、値を設定するときには、ByValである必要があります。これは、メモリ内のインスタンスをコピーして、この新しいインスタンスvalueに渡され、自分のコードがメモリにこのインスタンスを参照するように_Socketを設定している私の理解です。はい?

これが当てはまる場合、ネイティブタイプ以外のプロパティを使用する理由はわかりません。クラスのインスタンスをたくさんのメンバーでコピーすると、かなりのパフォーマンスが出ると思います。また、特にこのコードでは、コピーされたソケットインスタンスが実際には動作しないと思いますが、まだテストしていません。

とにかく、私の理解を確認するか、霧の論理の欠陥を説明することができれば、私は大いに感謝するでしょう。

答えて

44

リファレンスと値の型のコンセプトとByValByRefを混同していると思います。彼らの名前は少し誤解を招くものの、それは直交する問題です。 VB.NETの

は、指定された値のコピーが関数に送信されることを意味します。値タイプ(IntegerSingleなど)の場合、これは値の浅いコピーを提供します。より大きなタイプでは、これは非効率的である可能性があります。参照型の場合(String、クラスインスタンス)、参照のコピーが渡されます。コピーは=を介してパラメータに突然変異で渡されるため、呼び出し元の関数からは見えません。

ByRef VB.NETでは、元の値への参照が関数(1)に送信されることを意味します。これは、元の値が関数内で直接使用されているようなものです。 =のような操作は元の値に影響し、呼び出し元の関数ですぐに表示されます。

Socketは参考タイプ(読み込みクラス)なのでByValと一緒に渡すのが安いです。コピーを実行しても、インスタンスのコピーではなく、参照のコピーです。

(1)実際には、VB.NETはコールサイトでいくつかの種類のByRefをサポートしているため、これは100%ではありません。詳細については、ブログのエントリを参照してくださいThe many cases of ByRef


+0

1は、(上記のいくつかの行を埋めるために、私のコメント)も参照してください:[評価戦略](http://en.wikipedia.org/wiki/Evaluation_strategy) - ByRefのは、「参照による呼び出し」です(要約すると、「変数に代入して呼び出し元が渡した変数に影響を与えることができます」)、ByValは「値渡し」です。参照の*値が渡されたときにByValを渡した*参照型を変更することもできます(コピー/複製/複製は行われません)。 –

+1

+1 - 'リファレンスと値の型、ByValとByRefのコンセプトを混同していると思います。名前がちょっと誤解されているにもかかわらず、それらは直交した問題です。」C++から来て、これは私を捨てました。 – Jono

+0

優れた&詳細な返信 – Hardryv

10

ByValはまだ参照を渡すことを覚えておいてください。違いは、参照のコピーを取得することです。

私のオーバーロードされたコンストラクタで、私はSystem.Net.Sockets.Socketのインスタンスへの参照を受け入れますか?

代わりに、ByValを求めても同じことが言えます。違いは、ByValでは、参照のコピーを得る—あなたは新しい変数を持っています。 ByRefと同じ変数です。

メモリ内のインスタンスが

いやコピーされていることを私の理解です。参照のみがコピーされます。したがって、同じインスタンスので作業しています。

ここではより明確にそれを説明するコード例を示します。

Public Class Foo 
    Public Property Bar As String 
    Public Sub New(ByVal Bar As String) 
     Me.Bar = Bar 
    End Sub 
End Class 

Public Sub RefTest(ByRef Baz As Foo) 
    Baz.Bar = "Foo" 
    Baz = new Foo("replaced") 
End Sub 

Public Sub ValTest(ByVal Baz As Foo) 
    Baz.Bar = "Foo" 
    Baz = new Foo("replaced") 
End Sub 

Dim MyFoo As New Foo("-") 
RefTest(MyFoo) 
Console.WriteLine(MyFoo.Bar) ''# outputs replaced 

ValTest(MyFoo) 
Console.WriteLine(MyFoo.Bar) ''# outputs Foo 
+0

注意してください...あなたのBaz変数はあなたがそれを成立させるほど安全ではありません。 2つの方法の両方でコードを 'Baz.Bar =" replaced "に変更すると、MyFoo変数はByValとByRefバージョンの両方でヌークになります。多くのVB開発者を噛んだバグ。 Barプロパティにgetterしかなければ、それは不変になり、安全です。それが立つとバストは、コードが壊れています。 – mattmc3

+0

@ mattmc3 - 私はその違いを理解しています - MyFooを間違って初期化しました。今はっきりしているはずです。 –

3

私の理解では、いつものByVal/ByRefの決定は、実際に(スタックに)値型のために最も重要なこととなっています。 ByVal/ByRefは参照型がSystem.Stringのようなimmutableでなければ(ヒープ上の)参照型の違いはほとんどありません。変更可能なオブジェクトの場合は、オブジェクトByRefまたはByValを渡すかどうかは関係ありません。メソッド内で変更すると、呼び出し元の関数に変更が反映されます。

ソケットは変更可能ですので、任意の方法で渡すことができますが、オブジェクトを変更したくない場合は、自分で詳細なコピーを作成する必要があります。

Module Module1 

    Sub Main() 
     Dim i As Integer = 10 
     Console.WriteLine("initial value of int {0}:", i) 
     ByValInt(i) 
     Console.WriteLine("after byval value of int {0}:", i) 
     ByRefInt(i) 
     Console.WriteLine("after byref value of int {0}:", i) 

     Dim s As String = "hello" 
     Console.WriteLine("initial value of str {0}:", s) 
     ByValString(s) 
     Console.WriteLine("after byval value of str {0}:", s) 
     ByRefString(s) 
     Console.WriteLine("after byref value of str {0}:", s) 

     Dim sb As New System.Text.StringBuilder("hi") 
     Console.WriteLine("initial value of string builder {0}:", sb) 
     ByValStringBuilder(sb) 
     Console.WriteLine("after byval value of string builder {0}:", sb) 
     ByRefStringBuilder(sb) 
     Console.WriteLine("after byref value of string builder {0}:", sb) 

     Console.WriteLine("Done...") 
     Console.ReadKey(True) 
    End Sub 

    Sub ByValInt(ByVal value As Integer) 
     value += 1 
    End Sub 

    Sub ByRefInt(ByRef value As Integer) 
     value += 1 
    End Sub 

    Sub ByValString(ByVal value As String) 
     value += " world!" 
    End Sub 

    Sub ByRefString(ByRef value As String) 
     value += " world!" 
    End Sub 

    Sub ByValStringBuilder(ByVal value As System.Text.StringBuilder) 
     value.Append(" world!") 
    End Sub 

    Sub ByRefStringBuilder(ByRef value As System.Text.StringBuilder) 
     value.Append(" world!") 
    End Sub 

End Module 
+0

はい、私はこれに同意し、 'HttpClient' _ByRef_という考えを渡してしまったばかりです。私はローカルの認証ヘッダーだけを変更することができました。いいえ。呼び出し側プロシージャのAuthヘッダも変更されます。私は、_ByVal_の基本的な理由の1つ(_ByRef_とは対照的に)が、あなたが欲しがっていることをあなたが気に入らないようにすることができることを期待していたので、あまりにも面倒です。 – SteveCinq

関連する問題