Question

Je viens juste de commencer sur une classe pour gérer les connexions client à un serveur TCP. Voici le code que j'ai écrit jusqu'à présent:

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

Alors, mon constructeur surchargée, j'accepte référence à une par exemple d'un System.Net.Sockets.Socket, oui?

Maintenant, sur ma propriété Socket, le réglage de la valeur, il est nécessaire d'être ByVal. Je crois comprendre que le par exemple dans la mémoire est Copié , et ce nouvelle instance est passé à value, et mes ensembles de code _Socket pour faire référence à cette par exemple dans la mémoire. Oui?

Si cela est vrai, alors je ne vois pas pourquoi je voudrais utiliser les propriétés pour tout sauf les types natifs. J'imagine qu'il peut y avoir un certain impact sur les performances si la copie des instances de classe avec beaucoup de membres. En outre, pour ce code en particulier, j'imagine une instance de socket copié ne serait pas vraiment le travail, mais je ne l'ai pas encore testé.

Quoi qu'il en soit, si vous pouvez soit confirmer ma compréhension, ou d'expliquer les défauts dans ma logique de brouillard, je serais très heureux.

Était-ce utile?

La solution

Je pense que vous confondez le concept de références par rapport à des types de valeur et ByVal par rapport à ByRef. Même si leurs noms sont un peu trompeur, ce sont des questions orthogonales.

ByVal dans des moyens de VB.NET qu'une copie de la valeur fournie sera envoyée à la fonction. Pour les types de valeur (Integer, Single, etc.) cela fournira une copie peu profonde de la valeur. Avec les types plus grands, cela peut être inefficace. Pour les types de référence (quoique de String, des instances de classe) une copie de la référence est passée. Du fait une copie est passée dans les mutations au paramètre via = il ne sera pas visible à la fonction d'appel.

ByRef dans des moyens de VB.NET qu'une référence à la valeur d'origine sera envoyée à la fonction (1). Il est presque comme la valeur d'origine est utilisé directement dans la fonction. Les opérations telles que = auront une incidence sur la valeur d'origine et être immédiatement visible dans la fonction d'appel.

Socket est un type de référence (classe lu) et il passe donc avec ByVal ne coûte pas cher. Même si elle fait effectuer une copie, il est une copie de la référence, pas une copie de l'instance.

(1) Ce n'est pas vrai à 100% mais parce que VB.NET soutient en fait plusieurs types de ByRef au callsite. Pour plus de détails, voir l'entrée de blog Les nombreux cas de ByRef


Autres conseils

Rappelez-vous que ByVal passe toujours des références. La différence est que vous obtenez une copie de la référence.

  

Alors, mon constructeur surchargée, j'accepte une référence à une instance d'un System.Net.Sockets.Socket, oui?

Oui, mais la même chose serait vrai si vous l'avez demandé ByVal à la place. La différence est que, avec ByVal vous obtenez une copie de la référence - vous avez nouvelle variable. Avec ByRef, c'est la même variable.

  

Je crois comprendre que l'instance en mémoire est copiée

Non. Seule la référence est copiée. Par conséquent, vous travaillez toujours avec même instance.

Voici un exemple de code qui explique plus clairement:

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

Si je comprends bien a toujours été que le ByVal / ByRef décision est vraiment important pour la plupart des types de valeur (sur la pile). ByVal / ByRef fait très peu de différence du tout pour les types de référence (sur le tas), sauf ce type de référence est immuable comme System.String. Pour les objets mutables, il n'a pas d'importance si vous passez un objet ou ByRef ByVal, si vous modifiez dans la méthode de la fonction d'appel verra les modifications.

Socket est mutable, de sorte que vous pouvez passer toute la façon dont vous voulez, mais si vous ne souhaitez pas conserver les modifications à l'objet que vous devez faire une copie en profondeur vous.

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

Considérez C, et la différence entre un scalaire, comme int, et un pointeur d'int, et un pointeur vers un pointeur d'int.

int a;
int* a1 = &a;
int** a2 = &a1;

Passe un est par valeur. Passe a1 est une référence à un; il est l'adresse d'un. Passe a2 est une référence à une référence; ce qui est passé est l'adresse de a1.

Le passage d'une variable en utilisant la liste ByRef est analogue au scénario a2. Il est déjà une référence. Vous passez une référence à une référence. Faire cela signifie que vous pouvez changer non seulement le contenu de la liste, vous pouvez peut changer le paramètre pour pointer vers une liste tout à fait différent. Cela signifie également que vous ne pouvez pas passer une valeur NULL littérale au lieu d'une instance de liste

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top