2016-11-28 6 views
6

INotifyPropertyChanged 인터페이스를 구현하는 기본 클래스를 만들었습니다. 이 클래스는 또한 모든 속성의 값을 설정하고 필요한 경우 PropertyChanged 이벤트를 발생시키는 일반 함수 SetProperty을 포함합니다.ByRef 매개 변수로 속성 대 변수

Public Class BaseClass 
    Implements INotifyPropertyChanged 

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged 

    Protected Function SetProperty(Of T)(ByRef storage As T, value As T, <CallerMemberName> Optional ByVal propertyName As String = Nothing) As Boolean 
     If Object.Equals(storage, value) Then 
      Return False 
     End If 

     storage = value 
     Me.OnPropertyChanged(propertyName) 
     Return True 
    End Function 

    Protected Overridable Sub OnPropertyChanged(<CallerMemberName> Optional ByVal propertyName As String = Nothing) 
     If String.IsNullOrEmpty(propertyName) Then 
      Throw New ArgumentNullException(NameOf(propertyName)) 
     End If 

     RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) 
    End Sub 

End Class 

그렇다면 일부 데이터를 보유하고있는 클래스가 있습니다. 단순화를 위해 여기에는 하나의 특성 만 포함됩니다 (이 예제에서).

Public Class Item 
    Public Property Text As String 
End Class 

그렇다면 기본 클래스에서 상속 받고 데이터 보관 클래스를 사용하는 세 번째 클래스가 있습니다. 이 세 번째 클래스는 WPF 윈도우의 ViewModel입니다.

RelayCommand 클래스의 코드는 나와 있지 않습니다. 아마도 모든 구현은 직접 구현되어 있기 때문입니다. 이 클래스는 명령이 실행될 때 주어진 함수를 실행한다는 것을 명심하십시오.

Public Class ViewModel 
    Inherits BaseClass 

    Private _text1 As Item 'data holding class 
    Private _text2 As String 'simple variable 
    Private _testCommand As ICommand = New RelayCommand(AddressOf Me.Test) 

    Public Sub New() 
     _text1 = New Item 
    End Sub 

    Public Property Text1 As String 
     Get 
      Return _text1.Text 
     End Get 
     Set(ByVal value As String) 
      Me.SetProperty(Of String)(_text1.Text, value) 
     End Set 
    End Property 

    Public Property Text2 As String 
     Get 
      Return _text2 
     End Get 
     Set(ByVal value As String) 
      Me.SetProperty(Of String)(_text2, value) 
     End Set 
    End Property 

    Public ReadOnly Property TestCommand As ICommand 
     Get 
      Return _testCommand 
     End Get 
    End Property 

    Private Sub Test() 
     Me.Text1 = "Text1" 
     Me.Text2 = "Text2" 
    End Sub 

End Class 

그리고 나는 그것의 DataContext과 뷰 모델 클래스를 사용하여 내 WPF 창을 가지고있다.

<Window x:Class="MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:WpfTest" 
     mc:Ignorable="d" 
     Title="MainWindow" Height="350" Width="525"> 
    <Window.DataContext> 
     <local:ViewModel /> 
    </Window.DataContext> 

    <StackPanel Orientation="Horizontal"> 
     <TextBox Text="{Binding Text1}" Height="24" Width="100" /> 
     <TextBox Text="{Binding Text2}" Height="24" Width="100" /> 
     <Button Height="24" Content="Fill" Command="{Binding TestCommand}" /> 
    </StackPanel> 
</Window> 

이 창에는 두 개의 텍스트 상자와 버튼 만 있습니다. TextBoxes는 Text1Text2 속성에 바인딩되며 단추는 TestCommand 명령을 실행하기로되어 있습니다.

명령을 실행하면 Text1Text2 속성 모두 값이 제공됩니다. 두 속성 모두 PropertyChanged 이벤트를 발생시키기 때문에 이러한 값은 내 창에 표시되어야합니다.

그러나 "Text2"값만 내 창에 표시됩니다.

속성의 값이 Text1인데 "Text1"이지만이 속성에 대한 PropertyChanged 이벤트가 속성 값보다 높아진 것 같습니다.

내 기본 클래스에서 SetProperty 함수를 변경하여 속성 값을 얻은 후 PropertyChanged을 발생시킬 수 있습니까?

도움 주셔서 감사합니다.

+1

http://stackoverflow.com/a/4520101/17034 –

답변

4

실제로 어떤 일이 발생합니까?

속성이 필드처럼 동작하지 않으므로 작동하지 않습니다. 당신이 Me.SetProperty(Of String)(_text2, value)을 수행 할 때

, 무슨 일이 것은 SetProperty 기능이 기준 안에 무엇을 수정할 수 있도록 필드 _text2에 대한 참조가 대신 그 값의 전달, 그리고 필드가 수정 될 것입니다. 당신이 Me.SetProperty(Of String)(_text1.Text, value)을 수행 할 때

그러나, 컴파일러는 속성에 대한 게터를보고, 그래서 먼저 다음, _text1의 속성을 가져 매개 변수와 반환 값에 대한 참조를 전달 호출합니다. 따라서 함수 SetPropertyByRef 매개 변수를 수신하면 getter의 반환 값은 이고 실제 필드 값은이 아닙니다.

here에서 귀하의 속성이 ByRef라고 말하면 컴파일러는 함수 호출을 종료 할 때 필드 참조를 자동으로 변경합니다 ... 따라서 귀하의 이벤트 이후에 변경되는 이유를 설명 할 것입니다 ...

This other blog이 이상한 동작을 확인하는 것 같습니다.

+0

그러나 속성의 값은 변경되지만 SetProperty 함수가 남아있을 때만 변경됩니다. – Nostromo

+0

좋은 그 기사와 함께 찾으십시오. 공식적이든 아니든, 각각에'Trace.WriteLine()'을 가진'Item.Text' 명시 적 getter/setter를 주면 내가 보는 행동을 정확하게 묘사합니다. –

3

C#에서는 동일한 코드가 컴파일되지 않습니다. .NET은 참조로 속성을 전달하는 것이 쉽지 않습니다. 에릭 리 퍼트 (Eric Lippert)와 같은 사람들이 다른 곳으로 갔기 때문에 (에릭이 C# 어딘가에있는 문제를 해결했지만 어쨌든 찾을 수는 없습니다. C# 팀이 받아 들일 수없는 것으로 간주하는 단점이있는 이상한 해결 방법이나 다른 해결 방법이 필요합니다.

VB하지만, 다소 이상한 특수한 경우로 : 내가 볼 수있는 동작은 참조로 전달 된 임시 변수를 만든 다음 그 값을 속성에 할당하면 메소드가 완료됩니다. 이것은/ref이 .NET에서 구현되는 방법을 모르는 사람들에게 부작용이있는 해결책 인 @Martin Verjans의 우수 답변을 참고하십시오.

VB.NET 및 C# (및 F # 및 IronPython 등이 해당하므로 이 제대로 작동하지 않을 수 있습니다.)는 상호 호환 가능해야하므로 VB ByRef 매개 변수는 C# 코드에서 전달 된 C# ref 인수와 호환되어야합니다. 따라서 모든 해결 방법은 전적으로 발신자의 책임이어야합니다. 정당성의 한계 내에서, 그것은 전화가 시작되기 전에 할 수있는 일과 그것이 돌아온 후에 할 수있는 일로 제한합니다.

다음은 ECMA 335 (Common Language Infrastructure) standard는 ("byref"에 대한 Ctrl 키 + F 검색)를 말해야하는 내용은 다음과 같습니다

  • § I.8.2.1.1   관리 포인터와 관련 유형

    관리되는 포인터 (§I.12.1.1.2) 또는 이 참조 (§I.8.6.1.3, §I.12.4.1.5.2)은 지역 변수, 매개 변수, 복합 유형의 필드 또는 배열의 요소. ... 환언

는 멀리 컴파일러에 관한 한 ByRef storage As T 실제로 코드 값을두고 메모리의 저장 위치의 어드레스이다. 런타임시 매우 효율적이지만 getter 및 setter를 사용하는 구문 설탕 마법의 범위는 제공하지 않습니다. 속성 입니다. 한 쌍의 메소드, getter와 setter (또는 물론 하나 또는 다른 하나)입니다.

설명대로 storageSetProperty() 안에 새 값을 가져오고 SetProperty()이 완료되면 _text1.Text에 새 값이 있습니다. 그러나 컴파일러는 이벤트의 실제 순서가 예상대로되지 않게하는 몇 가지 오컬트 헛소리를 소개했습니다.

결과적으로 SetPropertyText1 작성 방법에 사용할 수 없습니다. 내가 테스트 한 가장 간단한 해결 방법은 Text1에 대한 설정 도구에서 OnPropertyChanged()으로 직접 전화하는 것입니다.

Public Property Text1 As String 
    Get 
     Return _text1.Text 
    End Get 
    Set(ByVal value As String) 
     _text1.Text = value 
     Me.OnPropertyChanged() 
    End Set 
End Property 

적어도 조금 추한 것은 아닙니다. Text2처럼 Text1에 정기적 인 배경 필드를 부여 할 수 있지만 그 후에는 _text1.Text과 동기화해야합니다. 위의 IMO보다 더 추한 이유는 두 개를 동기화 상태로 유지해야하고 사용자가 Text1 설정 도구에 여분의 코드가 남아 있기 때문입니다.

+0

VB.NET에서 속성을 전달하는 것이 불가능한 지 잘 모르겠습니다. 방금 주어진 시나리오를 테스트했고 속성 값이 참조로 전달 될 때 변경되었습니다. – Streamline

+0

@Streamline 감사합니다. 지금 몇 가지 테스트 코드를 작성하고 있습니다. –

+0

@Streamline 당신이 맞습니다. 값을 바꾸지 만, 'SetProperty'가 끝난 후에 OP가 묘사하는 것과 똑같이. 아주 이상한. –