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
とByRef
の概念が混乱していると思います。それらの名前は少し誤解を招くかもしれませんが、それらは直交する問題です。
VB.NETのByVal
は、提供された値のコピーが関数に送信されることを意味します。値タイプ(Integer
、Single
など)の場合、これは値の浅いコピーを提供します。より大きな型では、これは非効率的です。 (String
、クラスインスタンス)の参照型の場合、参照のコピーが渡されます。コピーは=
を介してミューテーションでパラメーターに渡されるため、呼び出し元の関数からは見えません。
VB.NETのByRef
は、元の値への参照が関数に送信されることを意味します(1)。これは、元の値が関数内で直接使用されているようなものです。 =
のような操作は元の値に影響し、呼び出し元の関数ですぐに表示されます。
Socket
は参照型(読み取りクラス)であるため、ByVal
を指定して渡すと簡単です。コピーは実行されますが、インスタンスのコピーではなく、参照のコピーです。
(1)VB.NETは実際にはコールサイトで数種類の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の決定は(スタック上の)値型にとって最も重要であると常に理解してきました。 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
Cについて考えてみてください。intのようなスカラーと、intポインターと、intポインターへのポインターとの違いです。
int a;
int* a1 = &a;
int** a2 = &a1;
Aを渡すのは値渡しです。 a1を渡すことはaへの参照です。のアドレスです。 a2を渡すことは参照への参照です。渡されるのはa1のアドレスです。
ByRefを使用してリスト変数を渡すことは、a2シナリオに類似しています。既に参考です。参照を参照に渡している。これにより、リストの内容を変更できるだけでなく、完全に異なるリストを指すようにパラメーターを変更できます。また、Listのインスタンスの代わりにリテラルnullを渡すことができないことも意味します