IT

VB.NET '위드' 선언문 - 포용할 것인가, 피할 것인가?

itgroup 2023. 5. 18. 20:58
반응형

VB.NET '위드' 선언문 - 포용할 것인가, 피할 것인가?

저는 작업 중에 특정 물체의 수많은 속성을 공사 중에 또는 수명 초기에 설정해야 하는 프로젝트를 자주 수행하고 있습니다., 과가을독성위해, 저는종다사다니용합음을을 합니다.With이러한 속성을 설정하는 문입니다.을 발견합니다.

With Me.Elements
    .PropertyA = True
    .PropertyB = "Inactive"
    ' And so on for several more lines
End With

보다 훨씬 좋아 보입니다.

Me.Elements.PropertyA = True
Me.Elements.PropertyB = "Inactive"
' And so on for several more lines

단순히 속성을 설정하는 매우 긴 문입니다.

사용하는 데 몇 가지 문제가 있다는 것을 알게 되었습니다.With디버깅하는 동안; 하지만 실제로 사용하는 것을 피해야 하는 강력한 이유가 있는지 궁금했습니다.저는 항상 위의 두 경우에 대해 컴파일러를 통해 생성된 코드가 기본적으로 동일하다고 가정했기 때문에 항상 제가 더 읽기 쉽다고 느끼는 것을 쓰기로 선택했습니다.

변수 이름이 길고 다음과 같이 끝나는 경우:

UserHandler.GetUser.First.User.FirstName="Stefan"
UserHandler.GetUser.First.User.LastName="Karlsson"
UserHandler.GetUser.First.User.Age="39"
UserHandler.GetUser.First.User.Sex="Male"
UserHandler.GetUser.First.User.Occupation="Programmer"
UserHandler.GetUser.First.User.UserID="0"
....and so on

그런 다음 WITH를 사용하여 더 읽기 쉽게 만들 것입니다.

With UserHandler.GetUser.First.User
    .FirstName="Stefan"
    .LastName="Karlsson"
    .Age="39"
    .Sex="Male"
    .Occupation="Programmer"
    .UserID="0"
end with

후자의 예에서는 첫 번째 예보다 성능 면에서 더 큰 이점이 있습니다. 첫 번째 예에서는 사용자 속성에 액세스할 때마다 사용자를 가져오고 WITH의 경우에는 사용자를 한 번만 가져오기 때문입니다.

다음과 같이 사용하지 않고도 성능을 향상시킬 수 있습니다.

dim myuser as user =UserHandler.GetUser.First.User
myuser.FirstName="Stefan"
myuser.LastName="Karlsson"
myuser.Age="39"
myuser.Sex="Male"
myuser.Occupation="Programmer"
myuser.UserID="0"

하지만 저는 대신 WITH 문을 선택할 것입니다, 더 깨끗해 보입니다.

그리고 저는 이것을 예로 들었기 때문에 키워드가 많은 수업에 대해 불평하지 마십시오. 다른 예는 다음과 같습니다. WITH RefundDialog.환불 데이터 그리드 보기.선택한 행(0)

실제로, 그것에 반대하는 정말 설득력 있는 요점은 없습니다.팬은 아니지만, 그건 개인적인 선호입니다, 그것을 암시하는 경험적인 자료는 없습니다.With구성이 잘못되었습니다.

.NET에서는 객체 이름을 완전 수식하는 것과 정확히 동일한 코드로 컴파일되므로 이 설탕에 대한 성능 저하는 없습니다.다음 VB .NET 2.0 클래스를 컴파일하고 분해하여 이를 확인했습니다.

Imports System.Text

Public Class Class1
    Public Sub Foo()
        Dim sb As New StringBuilder
        With sb
            .Append("foo")
            .Append("bar")
            .Append("zap")
        End With

        Dim sb2 As New StringBuilder
        sb2.Append("foo")
        sb2.Append("bar")
        sb2.Append("zap")
    End Sub
End Class

디스어셈블리는 다음과 같습니다. 호출 내용은 다음과 같습니다.sb2Append는 방은동게보입다니하일과 .With는 을는하성서명을 합니다.sb:

.method public instance void  Foo() cil managed
{
  // Code size       91 (0x5b)
  .maxstack  2
  .locals init ([0] class [mscorlib]System.Text.StringBuilder sb,
           [1] class [mscorlib]System.Text.StringBuilder sb2,
           [2] class [mscorlib]System.Text.StringBuilder VB$t_ref$L0)
  IL_0000:  nop
  IL_0001:  newobj     instance void [mscorlib]System.Text.StringBuilder::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  stloc.2
  IL_0009:  ldloc.2
  IL_000a:  ldstr      "foo"
  IL_000f:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_0014:  pop
  IL_0015:  ldloc.2
  IL_0016:  ldstr      "bar"
  IL_001b:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_0020:  pop
  IL_0021:  ldloc.2
  IL_0022:  ldstr      "zap"
  IL_0027:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_002c:  pop
  IL_002d:  ldnull
  IL_002e:  stloc.2
  IL_002f:  newobj     instance void [mscorlib]System.Text.StringBuilder::.ctor()
  IL_0034:  stloc.1
  IL_0035:  ldloc.1
  IL_0036:  ldstr      "foo"
  IL_003b:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_0040:  pop
  IL_0041:  ldloc.1
  IL_0042:  ldstr      "bar"
  IL_0047:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_004c:  pop
  IL_004d:  ldloc.1
  IL_004e:  ldstr      "zap"
  IL_0053:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_0058:  pop
  IL_0059:  nop
  IL_005a:  ret
} // end of method Class1::Foo

그래서 만약 여러분이 그것을 좋아하고, 더 읽기 쉽다고 생각한다면, 그것을 시도해 보세요; 그렇게 하지 않을 강력한 이유는 없습니다.

(그런데, , 저는 디버거에 무슨 일이 일어났는지 알고 싶습니다. 저는 디버거에서 어떤 특이한 행동을 본 기억이 없습니다.With진술, 그래서 저는 당신이 어떤 행동을 보였는지 궁금합니다.)

With를 사용하는 것과 객체를 반복적으로 참조하는 것 사이에는 차이가 있는데, 이는 미묘하지만 염두에 두어야 한다고 생각합니다.

WITH 문을 사용하면 개체를 참조하는 새 로컬 변수가 생성됩니다..xx를 사용하는 후속 참조는 해당 로컬 참조의 속성에 대한 참조입니다.WITH 문을 실행하는 동안 원래 변수 참조가 변경되는 경우 WITH에서 참조하는 개체는 변경되지 않습니다.고려 사항:

Dim AA As AAClass = GetNextAAObject()
With AA
    AA = GetNextAAObject()

    '// Setting property of original AA instance, not later instance
    .SomeProperty = SomeValue
End With

따라서 WITH 진술은 단순히 구문적인 설탕이 아니라 진정으로 다른 구성입니다.위와 같은 명시적인 코드를 작성할 가능성은 낮지만 일부 상황에서는 실수로 이 문제가 발생할 수 있으므로 문제를 인지해야 합니다.가장 가능성이 높은 상황은 속성을 설정하여 상호 연결이 암시적으로 변경되는 객체의 네트워크와 같은 구조물을 통과하는 경우입니다.

가독성이 중요합니다.모든 통사적 설탕과 마찬가지로, 그것은 과도하게 사용될 수 있습니다.

개체의 여러 구성원을 몇 줄에 걸쳐 설정하는 경우 이 옵션을 사용합니다.

With myObject
  .Property1 = arg1
  .Property2 = arg2
...

"With"로 다른 작업을 수행하지 않도록 합니다.

50-100줄에 이르는 블록을 작성하고 다른 변수를 많이 포함하는 경우 블록 맨 위에 선언된 내용을 기억하는 것이 매우 어려울 수 있습니다.명백한 이유로, 저는 그러한 지저분한 코드의 예를 제공하지 않을 것입니다.

코드를 더 쉽게 읽을 수 있는 곳으로 이동합니다.가독성이 떨어지는 경우에는 피하십시오. 특히 문장으로 중첩하는 것은 피하는 것이 좋습니다.

C# 3.0에는 개체 초기화 전용으로 다음 기능이 있습니다.

var x = new Whatever { PropertyA=true, PropertyB="Inactive" };

이는 LINQ에 매우 많이 필요할 뿐만 아니라 구문이 코드 냄새를 나타내지 않는 부분에서도 의미가 있습니다.일반적으로 초기 구성을 초과하여 개체에 대해 많은 다양한 작업을 수행할 때 해당 작업은 개체 자체에 대해 단일 작업으로 캡슐화되어야 합니다.

당신의 예에 대한 한 가지 메모 - 당신은 정말 "나"가 필요합니까?그냥 쓰는 게 어때요?

PropertyA = True
PropertyB = "Inactive"

확실히 '나'는 그 경우에 암시되어 있습니다...

나는 이 키워드를 많이 사용하는 코드가 의심스러울 것입니다: 만약 이것이 많은 인스턴스 변수나 속성을 쉽게 설정하는 데 사용된다면, 이것은 클래스가 너무 크다는 것을 나타낼 수 있다고 생각합니다(Large Class 냄새).이 기능을 사용하여 다음과 같은 긴 통화 체인을 대체하는 경우:

UserHandler.GetUser.First.User.FirstName="Stefan"
UserHandler.GetUser.First.User.LastName="Karlsson"
UserHandler.GetUser.First.User.Age="39"
UserHandler.GetUser.First.User.Sex="Male"
UserHandler.GetUser.First.User.Occupation="Programmer"
UserHandler.GetUser.First.User.UserID="0"

그렇다면 당신은 아마도 데메터 법을 위반하고 있을 것입니다.

저는 VB.NET을 사용하지 않지만...

선행 점은 필수 사항입니까?그렇다면 문제가 없다고 봅니다.Javascript에서 사용한 결과입니다.with객체의 속성이 일반 변수와 똑같이 생겼다는 것입니다. 속성에 액세스하는지 변수에 액세스하는지 여부를 볼 수 없기 때문에 매우 위험합니다.with피해야 할 것입니다.

객체를 사용하는 것이 눈에 더 쉬울 뿐만 아니라 객체의 속성에 반복적으로 액세스하는 경우 객체가 모든 속성에 대해 한 번이 아니라 메서드 체인을 통해 한 번만 가져오기 때문에 더 빠를 수 있습니다.

중첩된 사용을 피해야 한다는 다른 답변에 동의합니다.with피해야 할 이유와 같은 이유로with모두 Javascript로 되어 있습니다. 속성이 어떤 개체에 속하는지 더 이상 볼 수 없기 때문입니다.

'with'는 기본적으로 Smalltalk의 'cascade'입니다.이것은 Kent Beck의 Smalltalk Best Practice Patterns 책에 있는 패턴입니다.

패턴 요약: 개체로 전송된 메시지를 그룹화하는 것이 적절할 때 사용합니다.일부 메시지가 동일한 개체로 전송된 경우에는 사용하지 마십시오.

WITH 블록은 어떤 대가를 치르더라도 사용하지 마십시오(가독성까지).두 가지 이유:

  1. 마이크로소프트 문서의 ...에 대한 내용End With는 일부 상황에서는 스택에 데이터 복사본이 생성되므로 변경 내용이 삭제될 수 있다고 말합니다.
  2. LINQ 쿼리에 사용하는 경우 람다 결과는 체인이 아니므로 각 중간 절의 결과는 버려집니다.

이것을 설명하기 위해, 우리는 제 동료가 저자에게 물어봐야 했던 교과서의 (깨진) 예를 가지고 있습니다. (그것은 정말로 잘못된 것입니다. 보호하기 위해 이름이 변경되었습니다...무엇이든):

dbcontext 포함.블래쉬
.OrderBy(함수(현재 Blah)) currentBlah.성)
.그 다음 By(함수(현재 Blah) 전류 Blah.이름)
로드()
다음으로 끝 부분

OrderBy 및 ThenBy는 아무런 효과가 없습니다. With 및 EndWith를 삭제하고 처음 세 줄 끝에 줄 연속 문자를 추가하여 코드를 다시 포맷하면...작동합니다(같은 교과서의 15페이지 후반에 표시됨).

블록으로 검색하고 파괴할 이유가 더 이상 필요하지 않습니다.그들은 해석된 틀에서만 의미가 있었습니다.

구조체와 함께 사용할 때는 "with" 식의 로컬 복사본(블록을 사용하여 입력할 때 작성됨)을 작업하고 이 경우 (copy of object reference)로 작업하지 않으므로 필드를 설정할 수 없습니다.

objectExpression의 데이터 유형은 임의의 클래스 또는 구조 유형이거나 Integer와 같은 Visual Basic 기본 유형일 수 있습니다.objectExpression이 객체가 아닌 다른 값을 생성하는 경우 해당 멤버의 값만 읽거나 메서드를 호출할 수 있으며 With...에 사용되는 구조체의 멤버에 값을 할당하려고 하면 오류가 발생합니다.문으로 끝냅니다.이는 구조를 반환하고 즉시 액세스하여 Get과 같은 함수 결과의 멤버에게 값을 할당하는 메서드를 호출한 경우 발생하는 오류와 동일합니다.점().x = 1.두 경우 모두 구조가 콜 스택에만 존재하고, 이러한 상황에서 수정된 구조 구성원이 프로그램의 다른 코드가 변경을 관찰할 수 있는 위치에 쓸 수 있는 방법이 없다는 것이 문제입니다.

objectExpression은 블록 진입 시 한 번 평가됩니다.With 블록 내에서 objectExpression을 재할당할 수 없습니다.

https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/with-end-with-statement

구조를 반환하는 식 대신 구조 이름을 문으로 전달했다면 컴파일러가 조금 더 영리했을 수 있지만 그렇지 않은 것 같습니다.

언급URL : https://stackoverflow.com/questions/283749/the-vb-net-with-statement-embrace-or-avoid

반응형