2010-12-08 2 views
14

TCP 서버에 대한 클라이언트 연결을 처리하기위한 클래스를 시작하기 시작했습니다. 여기에 내가 지금까지 코드를 작성했습니다되어ByRef 대 ByVal 설명

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에서

ByVal은 제공된 값의 복사본이 함수로 전송된다는 것을 의미합니다. 값 유형 (Integer, Single 등)의 경우 값의 얕은 사본을 제공합니다. 더 큰 유형의 경우 비효율적 일 수 있습니다. 참조 유형의 경우 (String, 클래스 인스턴스) 참조 사본이 전달됩니다. 복제본은 =을 통해 매개 변수로 전달되므로 호출하는 함수에서 볼 수 없습니다.

ByRef VB.NET에서 원래 값에 대한 참조가 함수 (1)로 전송됨을 의미합니다. 그것은 원래 값이 함수 내에서 직접 사용되는 것과 거의 같습니다. =과 같은 작업은 원래 값에 영향을 미치고 호출 기능에서 즉시 볼 수 있습니다.

Socket은 참조 유형 (읽기 클래스)이므로 ByVal으로 전달하는 것이 저렴합니다. 사본을 수행하더라도 참조의 사본이지 인스턴스의 사본이 아닙니다.

(1) 이것은 실제로 VB.NET이 callsite에서 여러 종류의 ByRef를 지원하기 때문에 100 % 사실이 아닙니다. 자세한 내용은 The many cases of ByRef


+0

1도 참조 (내 의견이 위의 몇 줄을 채우기 위해) : 평가 전략은] (http://en.wikipedia.org/wiki/Evaluation_strategy은) -하는 ByRef은 '참조에 의한 호출'입니다 (요약하면 "변수에 할당하여 호출자가 전달한 변수에 영향을 미칠 수 있습니다") ByVal은 '값으로 호출'입니다. 참조 * 값이 전달 될 때 * ByteVal 전달 된 참조 형식을 여전히 변경할 수 있습니다. * 개체 자체의 복사/복제/복제가 발생하지 않습니다. –

+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 변수는 당신이하는 것처럼 안전하지 못합니다. 두 가지 방법 모두에서 코드를'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_에 대한 기본 이유 중 하나가 (_ByRef_와는 대조적으로) 그래서 당신이 콧 속적으로 당신이 원하는 것을 할 수 있다고 예상 했으므로 매우 짜증 스럽습니다. – SteveCinq