Вопрос

Я только начинаю создавать класс для обработки клиентских подключений к 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

Итак, в моем перегруженном конструкторе я принимаю ссылка к экземпляр из a System.Net.Sockets.Socket, да?

Теперь, на моем Socket свойство, при установке значения, обязательно должно быть ByVal.Насколько я понимаю, экземпляр в памяти есть скопированный, и это новый экземпляр передается в value, и мой код устанавливает _Socket чтобы сослаться на этот экземпляр в памяти.Да?

Если это правда, то я не понимаю, почему я хотел бы использовать свойства для чего-либо, кроме собственных типов.Я бы предположил, что копирование экземпляров класса с большим количеством членов может привести к значительному снижению производительности.Кроме того, в частности, для этого кода я бы предположил, что скопированный экземпляр сокета на самом деле не будет работать, но я его еще не тестировал.

В любом случае, если бы вы могли либо подтвердить мое понимание, либо объяснить недостатки в моей туманной логике, я был бы вам очень признателен.

Это было полезно?

Решение

Я думаю, вы путаете понятия ссылок итипы значений и ByVal против. ByRef.Несмотря на то, что их названия немного вводят в заблуждение, это ортогональные проблемы.

ByVal in VB.NET означает, что копия предоставленного значения будет отправлена в функцию.Для типов значений (Integer, Single, и т.д.) Это обеспечит неглубокую копию значения.С более крупными типами это может быть неэффективно.Однако для ссылочных типов (String, экземпляры класса) передается копия ссылки.Поскольку копия передается в виде изменений параметру через = это не будет видно вызывающей функции.

ByRef in VB.NET означает, что в функцию (1) будет отправлена ссылка на исходное значение.Это почти похоже на то, что исходное значение используется непосредственно внутри функции.Операции , подобные = повлияет на исходное значение и будет сразу видно в вызывающей функции.

Socket является ссылочным типом (класс чтения) и, следовательно, передает его с ByVal стоит дешево.Даже если он выполняет копирование, это копия ссылки, а не копия экземпляра.

(1) Однако это не на 100% верно, потому что VB.NET на самом деле поддерживается несколько видов ByRef на сайте вызова.Для получения более подробной информации смотрите запись в блоге Многочисленные случаи 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 делает очень мало различий вообще для эталонных типов (на куче), если только этот тип ссылки не неизменный как система .String. Для мультипликационных объектов не имеет значения, если вы передаете объект Byref или ByVal, если вы измените его в способе, в способе функция вызова увидит модификации.

Socket MUSTAME, так что вы можете пройти какой-то, какой, который вы хотите, но если вы не хотите сохранять изменения в объекте, вам нужно сделать глубокую копию самостоятельно.

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 - это ссылка на; Это адрес. Прохождение A2 - это ссылка на ссылку; пропущено, это адрес A1.

Передача переменная списка с помощью Byref аналогична сценарию A2. Это уже ссылка. Вы передаете ссылку на ссылку. Это означает, что вы не только можете изменить содержимое списка, вы можете изменить параметр, чтобы указать на совершенно другой список. Это также означает, что вы не можете пройти буквальный нулевой вместо экземпляра списка

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top