PropertyChanged.Fody自動通知屬性外掛

soliang發表於2024-04-26

NuGet 安裝

安裝 PropertyChanged.Fody NuGet 包並更新 Fody NuGet 包:Install the PropertyChanged.Fody NuGet package and update the Fody NuGet package

PM> Install-Package Fody
PM> Install-Package PropertyChanged.Fody

這是必需的,因為 NuGet 始終預設為任何依賴項的最舊、最有缺陷的版本。Install-Package Fody

新增到FodyWeavers.xml

新增到FodyWeavers.xml<PropertyChanged/>

<Weavers>
  <PropertyChanged/>
</Weavers>

概述

注意:實現 INotifyPropertyChanged 的所有類都將通知程式碼注入到屬性設定器中。

在程式碼之前:

public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    
    public string GivenNames { get; set; }
    public string FamilyName { get; set; }
    public string FullName => $"{GivenNames} {FamilyName}";
}

編譯的內容:

public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    string givenNames;
    public string GivenNames
    {
        get => givenNames;
        set
        {
            if (value != givenNames)
            {
                givenNames = value;
                OnPropertyChanged(InternalEventArgsCache.GivenNames);
                OnPropertyChanged(InternalEventArgsCache.FullName);
            }
        }
    }

    string familyName;
    public string FamilyName
    {
        get => familyName;
        set 
        {
            if (value != familyName)
            {
                familyName = value;
                OnPropertyChanged(InternalEventArgsCache.FamilyName);
                OnPropertyChanged(InternalEventArgsCache.FullName);
            }
        }
    }

    public string FullName => $"{GivenNames} {FamilyName}";

    protected void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
    {
        PropertyChanged?.Invoke(this, eventArgs);
    }
}

internal static class InternalEventArgsCache
{
    internal static PropertyChangedEventArgs FamilyName = new PropertyChangedEventArgs("FamilyName");
    internal static PropertyChangedEventArgs FullName = new PropertyChangedEventArgs("FullName");
    internal static PropertyChangedEventArgs GivenNames = new PropertyChangedEventArgs("GivenNames");
}

(實際注入的型別和方法名稱不同)

程式碼生成器

從版本 4 開始,PropertyChanged.Fody 附帶了一個 C# 程式碼生成器,該生成器可以透過生成 基本實現的樣板直接作為原始碼為您服務。INotifyPropertyChanged

只需將實現或具有屬性的類標記為 和 生成器將新增必要的事件和事件呼叫器:INotifyPropertyChanged[AddINotifyPropertyChangedInterface]partial

例如,像這樣的類:

public partial class Class1 : INotifyPropertyChanged
{
    public int Property1 { get; set; }
    public int Property2 { get; set; }
}

將透過生成器補充以下內容:

public partial class Class1
{
    public event PropertyChangedEventHandler? PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }
    protected virtual void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
    {
        PropertyChanged?.Invoke(this, eventArgs);
    }
}
  • 僅支援類,不支援記錄。
  • 對於巢狀類,所有包含類也必須是分部類。
  • 程式碼生成器僅在 SDK 樣式的專案中正常工作

程式碼生成器配置

您可以透過專案檔案中的屬性配置程式碼生成器:

<PropertyGroup>
  <PropertyChangedAnalyzerConfiguration>
    <IsCodeGeneratorDisabled>false</IsCodeGeneratorDisabled>
    <EventInvokerName>OnPropertyChanged</EventInvokerName>
  </PropertyChangedAnalyzerConfiguration>
</PropertyGroup>
  • IsCodeGeneratorDisabled:設定為以關閉程式碼生成器。true
  • EventInvokerName:將事件呼叫程式方法的名稱從更改為收藏夾名稱。OnPropertyChanged

面向多個框架的 WPF 專案的解決方法:

面向多個框架的 WPF 專案可能會在編譯*_wpftmp.csproj

... error CS0111: Type 'SomeType' already defines a member called 'OnPropertyChanged' with the same parameter types

這可以透過將以下生成目標新增到專案中來解決:

<Target Name="RemoveDuplicateAnalyzers" BeforeTargets="CoreCompile">
  <!-- see https://github.com/dotnet/wpf/pull/6680 -->
  <RemoveDuplicates Inputs="@(Analyzer)">
    <Output
      TaskParameter="Filtered"
      ItemName="FilteredAnalyzer"/>
  </RemoveDuplicates>
  <ItemGroup>
    <Analyzer Remove="@(Analyzer)" />
    <Analyzer Include="@(FilteredAnalyzer)" />
  </ItemGroup>
</Target>

筆記

  • 依賴屬性 — 在上面的示例中,getter for 依賴於 和 的 getter 。因此,當設定了 or 時,也會為 as 提出。可以使用源屬性上的 AlsoNotifyFor 屬性或目標屬性上的 DependsOn 屬性手動配置此行為。FullNameGivenNameFamilyNameGivenNameFamilyNamePropertyChangedFullName

  • 攔截通知呼叫

    • 全域性攔截
    • 類級攔截 — 僅當類上沒有此類現有方法時,才會注入該方法;如果有這樣的方法,那麼對該方法的呼叫將被注入到設定器中 - 請參閱此處OnPropertyChanged
    • 屬性級攔截 — 對於給定屬性,如果存在 形式為 的方法,則該方法將被呼叫 — 請參閱此處On<PropertyName>Changed
  • 要獲取 before/after 值,請對 / 使用以下簽名:OnPropertyChangedOn<PropertyName>Changed

    public void OnPropertyChanged(string propertyName, object before, object after)
    
  • 若要防止特定類進行通知呼叫注入,請使用 DoNotNotify 屬性

  • 若要僅將重寫範圍限定為特定類,而不限於整個程式集,可以使用 FilterType 屬性。這會將常規行為從“選擇退出”更改為“選擇加入”。例:。該字串被解釋為正規表示式,您可以使用多個篩選器。如果任何過濾器匹配,則將編織一個類。[assembly: PropertyChanged.FilterType("My.Specific.OptIn.Namespace.")]

  • 可以使用 AddINotifyPropertyChangedInterfaceAttribute 屬性為特定類自動實現介面。提出有關“此屬性的行為不符合預期”的問題將導致 RTFM 並關閉問題。INotifyPropertyChanged

    • 對於部分方法,這將透過程式碼生成器完成,因此在編譯時可以使用實現。
  • 行為是透過屬性Weavers.xml檔案中的選項配置的。

有關詳細資訊,請參閱 Wiki 頁面

相關文章