简体   繁体   中英

Attribute hooking MSBuild/Roslyn

If I wanted to create a Attribute (derived from System.Attribute) that hooks into the .NET Build process and translates/converts a standard C# auto property like:

[Notify]
public string Name { get; set; }

to this code, which then is compiled:

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

How would I achive it? What would I have to do? How can I let the attribute hook into the Build? As you can see I have no clues at all of the Build process, nor of Roslyn. But I want to get rid of superfluous MVVM boilerplate code and no longer spent too much time for dull repetitive typing....

Thx, Chris

There's a Fody weaver for this, called PropertyChanged .

Fody is a system for modifying your code at the end of the compile process. The PropertyChanged weaver automatically impements change notification for all properties in all classes that implement INotifyPropertyChanged . It has a number of ways to control the generation using attributes, implementing methods, etc.

The example from their GitHub project page starts like this:

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

The weaver interprets the above and generates code like this:

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

Of course you won't have access to that version, since it happens somewhere around the end of the compilation pass and your source is unaffected. Debugging the code can be a little difficult.

To help with that the PropertyChanged weaver looks for pre-implemented methods matching various rules and generates code to call those. If you have an OnPropertyChanged method in the class it'll call that instead of generating a boilerplate version. Or you can add an OnFamilyNameChanged method to the class above and that will be called before the OnPropertyChanged method.

You can use Roslyn source generators for that: Source Generators Cookbook

There's an INotifyPropertyChanged example .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM