ObservableProperty 특성

ObservableProperty 형식은 주석이 추가된 필드에서 관찰 가능한 속성을 생성할 수 있는 특성입니다. 그 목적은 관찰 가능한 속성을 정의하는 데 필요한 상용구의 양을 크게 줄이는 것입니다.

참고 항목

작동하려면 주석이 추가된 필드가 필요한 INotifyPropertyChanged 인프라를 사용하는 부분 클래스에 있어야 합니다. 형식이 중첩된 경우 선언 구문 트리의 모든 형식도 부분 주석으로 주석을 추가해야 합니다. 이렇게 하지 않으면 생성기에서 요청된 관찰 가능한 속성을 사용하여 해당 형식의 다른 부분 선언을 생성할 수 없으므로 컴파일 오류가 발생합니다.

플랫폼 API:ObservableProperty, , NotifyPropertyChangedFor, NotifyDataErrorInfoNotifyCanExecuteChangedFor, NotifyPropertyChangedRecipients, ICommand, IRelayCommandObservableValidator, PropertyChangedMessage<T>,IMessenger

작동 방식

이 특성은 ObservableProperty 다음과 같이 부분 형식의 필드에 주석을 추가하는 데 사용할 수 있습니다.

[ObservableProperty]
private string? name;

그리고 다음과 같이 관찰 가능한 속성을 생성합니다.

public string? Name
{
    get => name;
    set => SetProperty(ref name, value);
}

또한 최적화된 구현으로 이 작업을 수행하므로 최종 결과가 더 빨라집니다.

참고 항목

생성된 속성의 이름은 필드 이름에 따라 만들어집니다. 생성기는 필드의 이름이 지정 lowerCamel_lowerCamel 되거나 m_lowerCamel적절한 .NET 명명 규칙을 따르도록 UpperCamel 변환된다고 가정합니다. 결과 속성에는 항상 공용 접근자가 있지만 표시 유형으로 필드를 선언할 수 있습니다(private 권장됨).

변경 시 코드 실행

생성된 코드는 실제로 이 코드보다 약간 더 복잡하며, 그 이유는 알림 논리에 연결하기 위해 구현할 수 있는 몇 가지 메서드도 노출하고, 속성이 업데이트될 때 및 필요한 경우 업데이트 직후에 추가 논리를 실행하기 때문입니다. 즉, 생성된 코드는 실제로 다음과 유사합니다.

public string? Name
{
    get => name;
    set
    {
        if (!EqualityComparer<string?>.Default.Equals(name, value))
        {
            string? oldValue = name;
            OnNameChanging(value);
            OnNameChanging(oldValue, value);
            OnPropertyChanging();
            name = value;
            OnNameChanged(value);
            OnNameChanged(oldValue, value);
            OnPropertyChanged();
        }
    }
}

partial void OnNameChanging(string? value);
partial void OnNameChanged(string? value);

partial void OnNameChanging(string? oldValue, string? newValue);
partial void OnNameChanged(string? oldValue, string? newValue);

이렇게 하면 이러한 메서드를 구현하여 추가 코드를 삽입할 수 있습니다. 처음 두 가지는 속성이 설정된 새 값만 참조해야 하는 일부 논리를 실행하려는 경우에 유용합니다. 다른 두 가지는 설정 중인 이전 값과 새 값 모두에서 일부 상태를 업데이트해야 하는 좀 더 복잡한 논리가 있을 때마다 유용합니다.

예를 들어 처음 두 오버로드를 사용하는 방법의 예는 다음과 같습니다.

[ObservableProperty]
private string? name;

partial void OnNameChanging(string? value)
{
    Console.WriteLine($"Name is about to change to {value}");
}

partial void OnNameChanged(string? value)
{
    Console.WriteLine($"Name has changed to {value}");
}

다른 두 오버로드를 사용하는 방법의 예는 다음과 같습니다.

[ObservableProperty]
private ChildViewModel? selectedItem;

partial void OnSelectedItemChanging(ChildViewModel? oldValue, ChildViewModel? newValue)
{
    if (oldValue is not null)
    {
        oldValue.IsSelected = true;
    }

    if (newValue is not null)
    {
        newValue.IsSelected = true;
    }
}

사용 가능한 메서드 중에서 원하는 수의 메서드만 자유롭게 구현할 수 있으며, 그 중 어느 것도 구현할 수 없습니다. 구현되지 않은 경우(또는 하나만 있는 경우) 전체 호출은 컴파일러에서 제거되므로 이 추가 기능이 필요하지 않은 경우 성능이 전혀 저하되지 않습니다.

참고 항목

생성된 메서드는 구현이 없는 부분 메서드 입니다. 즉, 메서드를 구현하도록 선택하는 경우 명시적 접근성을 지정할 수 없습니다. 즉, 이러한 메서드의 구현은 메서드로 선언 partial 되어야 하며 항상 암시적으로 프라이빗 접근성을 갖습니다. 명시적 접근성(예: 추가 public 또는 private)을 추가하려고 하면 C#에서 허용되지 않으므로 오류가 발생합니다.

종속 속성에 알림

변경 시 알림을 Name 발생하려는 속성이 있다고 FullName 상상해 보십시오. 다음과 같이 특성을 사용하여 NotifyPropertyChangedFor 수행할 수 있습니다.

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FullName))]
private string? name;

이렇게 하면 다음과 같은 생성된 속성이 생성됩니다.

public string? Name
{
    get => name;
    set
    {
        if (SetProperty(ref name, value))
        {
            OnPropertyChanged("FullName");
        }
    }
}

종속 명령 알림

실행 상태가 이 속성의 값에 종속된 명령이 있다고 가정합니다. 즉, 속성이 변경될 때마다 명령의 실행 상태를 무효화하고 다시 계산해야 합니다. 즉, ICommand.CanExecuteChanged 다시 발생해야 합니다. 특성을 사용하여 이 작업을 수행할 수 있습니다.NotifyCanExecuteChangedFor

[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(MyCommand))]
private string? name;

이렇게 하면 다음과 같은 생성된 속성이 생성됩니다.

public string? Name
{
    get => name;
    set
    {
        if (SetProperty(ref name, value))
        {
            MyCommand.NotifyCanExecuteChanged();
        }
    }
}

이 작업이 작동하려면 대상 명령이 일부 IRelayCommand 속성이어야 합니다.

속성 유효성 검사 요청

이 속성이 ObservableValidator에서 상속되는 형식으로 선언된 경우 유효성 검사 특성으로 주석을 달고 생성된 setter에 해당 속성에 대한 유효성 검사를 트리거하도록 요청할 수도 있습니다. 이 작업은 다음 특성을 사용하여 NotifyDataErrorInfo 수행할 수 있습니다.

[ObservableProperty]
[NotifyDataErrorInfo]
[Required]
[MinLength(2)] // Any other validation attributes too...
private string? name;

그러면 다음 속성이 생성됩니다.

public string? Name
{
    get => name;
    set
    {
        if (SetProperty(ref name, value))
        {
            ValidateProperty(value, "Value2");
        }
    }
}

그런 다음 생성된 ValidateProperty 호출은 속성의 유효성을 검사하고 개체의 ObservableValidator 상태를 업데이트하므로 UI 구성 요소가 이에 대응하고 유효성 검사 오류를 적절하게 표시할 수 있습니다.

참고 항목

기본적으로 상속 ValidationAttribute 되는 필드 특성만 생성된 속성으로 전달됩니다. 이는 데이터 유효성 검사 시나리오를 지원하기 위해 특별히 수행됩니다. 다른 모든 필드 특성은 무시되므로 현재 필드에 사용자 지정 특성을 추가하고 생성된 속성에도 적용할 수 없습니다. 필요한 경우(예: serialization 제어) 기존 수동 속성을 대신 사용하는 것이 좋습니다.

알림 메시지 보내기

속성이 상속 ObservableRecipient되는 형식으로 선언된 경우 특성을 사용하여 NotifyPropertyChangedRecipients 속성 변경에 대한 속성 변경 메시지를 보내는 코드도 삽입하도록 생성기에 지시할 수 있습니다. 이렇게 하면 등록된 받는 사람이 변경에 동적으로 대응할 수 있습니다. 즉, 다음 코드를 고려합니다.

[ObservableProperty]
[NotifyPropertyChangedRecipients]
private string? name;

그러면 다음 속성이 생성됩니다.

public string? Name
{
    get => name;
    set
    {
        string? oldValue = name;

        if (SetProperty(ref name, value))
        {
            Broadcast(oldValue, value);
        }
    }
}

그런 다음 생성된 Broadcast 호출은 현재 viewmodel에서 사용 중인 인스턴스를 사용하여 IMessenger 등록된 모든 구독자에게 새 PropertyChangedMessage<T> 호출을 보냅니다.

사용자 지정 특성 추가

경우에 따라 생성된 속성에 대한 일부 사용자 지정 특성도 사용하는 것이 유용할 수 있습니다. 이를 위해 주석이 추가된 필드에 특성 목록의 [property: ] 대상을 사용하면 되며, MVVM 도구 키트는 해당 특성을 생성된 속성에 자동으로 전달합니다.

예를 들어 다음과 같은 필드를 고려합니다.

[ObservableProperty]
[property: JsonRequired]
[property: JsonPropertyName("name")]
private string? username;

이렇게 하면 해당 두 [JsonRequired] 속성과 [JsonPropertyName("name")] 속성이 포함된 속성이 생성 Username 됩니다. 원하는 만큼 속성을 대상으로 하는 특성 목록을 사용할 수 있으며 모든 특성이 생성된 속성으로 전달됩니다.

예제

  • 샘플 앱(여러 UI 프레임워크의 경우)을 확인하여 작동 중인 MVVM 도구 키트를 확인합니다.
  • 단위 테스트에서 더 많은 예제를 찾을 수도 있습니다.