属性挂钩 MSBuild/Roslyn

Posted

技术标签:

【中文标题】属性挂钩 MSBuild/Roslyn【英文标题】:Attribute hooking MSBuild/Roslyn 【发布时间】:2021-08-24 01:41:23 【问题描述】:

如果我想创建一个属性(从 System.Attribute 派生),它可以连接到 .NET 构建过程并转换/转换标准 C# 自动属性,例如:

[Notify]
public string Name  get; set; 

到此代码,然后编译:

private string _nameField;
public string Name
 
     get => _nameField;
     set  
     
         if (!EqualityComparer<string>.Default.Equals(_nameField, value))
         
              _nameField = value;
              NotifyPropertyChanged(nameof(Name));
         
     

我将如何实现它?我该怎么办? 如何让属性挂钩到构建中? 如您所见,我对构建过程和 Roslyn 都一无所知。 但我想摆脱多余的 MVVM 样板代码,不再花太多时间进行枯燥的重复输入......

谢谢,克里斯

【问题讨论】:

查看 Fody(IL 重写)或源代码生成器。 (仅限 c#9) 【参考方案1】:

为此有一个Fody 织布工,称为PropertyChanged。

Fody 是一个用于在编译过程结束时修改代码的系统。 PropertyChanged 编织器自动为实现INotifyPropertyChanged 的所有类中的所有属性执行更改通知。它有多种方法可以使用属性、实现方法等来控制生成。

他们的 GitHub 项目页面中的示例是这样开始的:

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");

当然,您将无法访问该版本,因为它发生在编译过程结束时的某个地方,并且您的源不受影响。调试代码可能有点困难。

为了帮助实现这一点,PropertyChanged 编织器会查找与各种规则匹配的预实现方法并生成代码来调用这些方法。如果您在类中有OnPropertyChanged 方法,它将调用该方法而不是生成样板版本。或者您可以在上面的类中添加一个OnFamilyNameChanged 方法,该方法将在OnPropertyChanged 方法之前调用。

【讨论】:

【参考方案2】:

您可以为此使用 Roslyn 源代码生成器:Source Generators Cookbook

有一个INotifyPropertyChanged example。

【讨论】:

以上是关于属性挂钩 MSBuild/Roslyn的主要内容,如果未能解决你的问题,请参考以下文章

属性持久化的 SQLAlchemy ORM 事件挂钩

React - 访问在 useEffect 挂钩中设置的对象属性

无法读取未定义反应挂钩的属性“位置”

指令 Dragula 更新挂钩中的错误:“TypeError:无法读取未定义的属性 'drake'”

TypeError:无法读取未定义的属性“getPosts”-useQuery 挂钩,反应功能组件

ACF 覆盖使用 load_value 挂钩添加的 HTML 属性