DotNetAnalyzers / PropertyChangedAnalyzers

Roslyn analyzers for INotifyPropertyChanged
MIT License
44 stars 4 forks source link

Add INPC analyzer for constructor parameter members #73

Open tldrlol opened 6 years ago

tldrlol commented 6 years ago

Consider:

public class Foo {
  public int Value { get; set; }
}

public class FooViewModel {
  readonly Foo foo;
  public FooViewModel(Foo foo) {
    this.foo = foo;
  }
}

In addition to the existing analyzers, that implement the INPC interface, and bind directly to Foo, it may be useful to bind directly to members of Foo instead.

For example:

public class FooViewModel : INotifyPropertyChanged {
  readonly Foo foo;

  public FooViewModel(Foo foo) {
    this.foo = foo;
  }

  public event PropertyChangedEventHandler PropertyChanged;

  public int Value {
    get => this.foo.Value;
    set {
      if (value == this.foo.Value)
          return;
      this.foo.Value = value;
      this.OnPropertyChanged();
    }
  }

  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
}

If this is implemented it should also behave intelligently. It should not offer to create an INPC property again for the same field. This can either be done by name (convention), or static analysis of existing INPC compliant properties.

There should also be some form of granularity that of what should be implemented.

  1. By access modifiers, public/internal.
  2. By member type properties/fields.
tldrlol commented 6 years ago

When C# 8 is released/Default interface implementations feature is available consider alternative patterns.

For example, in pseudo C#8:

class Person {
  public string name;
  public int    age;
}

interface IHasPerson {
  Person { get; }
}

interface INotifyPerson : INotifyPropertyChanged, IHasPerson {

  public string Name {
    get { return Person.name; }
    set {
      if (Person.name == value)
        return;

      Person.name = value;
      PropertyChanged(nameof(Name));
    }
  }

  public string Age {
    get { return Person.age; }
    set {
      if (Person.age == value)
        return;

      Person.age = value;
      PropertyChanged(nameof(Age));
    }
  }

}

class PersonViewModel : INotifyPropertyChanged, IHasPerson, INotifyPerson, {
  public Person { get; set; }
}

Alternative patterns may emerge with the shapes/extensions language proposal.