PropertyChanged.Fody自动通知属性插件

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 页面

posted @ 2024-04-26 20:41  soliang  阅读(1)  评论(0编辑  收藏  举报