質問
TCPサーバーへのクライアント接続を処理するためにクラスを始めたばかりです。これが私がこれまでに書いたコードです:
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
このインスタンスをメモリ内で参照します。はい?
これが本当なら、ネイティブタイプ以外のプロパティを使用したい理由がわかりません。多くのメンバーとクラスインスタンスをコピーすると、かなりのパフォーマンスが発生する可能性があると思います。また、特にこのコードについては、コピーされたソケットインスタンスが実際には機能しないと思いますが、まだテストしていません。
とにかく、あなたが私の理解を確認するか、私の霧の論理の欠陥を説明することができれば、私はそれを大いに感謝します。
解決
参照の概念と値の種類を混乱させていると思います ByVal
vs。 ByRef
. 。彼らの名前は少し誤解を招くものですが、彼らは直交の問題です。
ByVal
VB.NETでは、提供された値のコピーが関数に送信されることを意味します。値タイプの場合(Integer
, Single
, 、など)これは、値の浅いコピーを提供します。より大きなタイプでは、これは非効率的です。ただし、参照タイプの場合(String
, 、クラスインスタンス)参照のコピーが渡されます。コピーが変異でパラメーターに渡されているため =
呼び出し関数には見えません。
ByRef
VB.NETでは、元の値への参照が関数に送信されることを意味します(1)。これは、元の値が関数内で直接使用されているようなものです。のような操作 =
元の値に影響を与え、呼び出し関数にすぐに表示されます。
Socket
参照タイプ(読み取りクラス)であるため、 ByVal
安いです。コピーを実行しますが、インスタンスのコピーではなく、リファレンスのコピーです。
(1)VB.NETは実際にCalliteでいくつかの種類のBYREFをサポートしているため、これは100%真実ではありません。詳細については、ブログエントリを参照してください Byrefの多くのケース
他のヒント
それを覚えておいてください 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
私の理解は、バイバル/バイレフの決定が(スタック上)に実際に最も重要であるということでした。 byval/byrefは、参照タイプがある場合を除き、参照タイプ(ヒープ上)ではほとんど違いをもたらしません 不変 System.Stringのように。可変オブジェクトの場合、オブジェクトを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
Cを考えてください。また、INTやINTポインターなどのスカラーの違い、およびINTポインターへのポインターを考えてください。
int a;
int* a1 = &a;
int** a2 = &a1;
Aを渡すことは価値によってです。 A1を渡すことは、Aへの参照です。それはaのアドレスです。 A2を渡すことは、参照への参照です。渡されるのは、A1のアドレスです。
BYREFを使用してリスト変数を渡すことは、A2シナリオに類似しています。すでに参照です。参照への参照を渡しています。それを行うことは、リストの内容を変更できるだけでなく、パラメーターを変更してまったく異なるリストを指すことができることを意味します。また、リストのインスタンスの代わりにリテラルヌルを渡すことができないことを意味します