751 Stimmen

Implementieren von INotifyPropertyChanged - gibt es einen besseren Weg?

Microsoft hätte etwas Flottes für INotifyPropertyChanged wie bei den automatischen Eigenschaften, geben Sie einfach {get; set; notify;} Ich halte das für sehr sinnvoll. Oder gibt es dabei irgendwelche Komplikationen?

Können wir selbst so etwas wie "notify" in unseren Eigenschaften implementieren. Gibt es eine anmutige Lösung für die Implementierung von INotifyPropertyChanged in Ihrer Klasse oder die einzige Möglichkeit, dies zu tun, besteht darin, die PropertyChanged Ereignis in jeder Eigenschaft.

Wenn nicht, können wir etwas schreiben, um den Code automatisch zu generieren, der die PropertyChanged Veranstaltung?

0 Stimmen

Siehe stackoverflow.com/questions/1329138/ für eine compilergeprüfte Art der Implementierung von INotifyPropertyChanged. Vermeidung von Eigenschaftsnamen als magische Zeichenkette.

8 Stimmen

9 Stimmen

2voto

Ian Ringrose Punkte 50437

Ich habe gerade gefunden ActiveSharp - Automatisches INotifyPropertyChanged Ich habe es noch nicht benutzt, aber es sieht gut aus.

Um aus der Website zu zitieren...


Benachrichtigungen über Eigenschaftsänderungen senden ohne Angabe des Eigenschaftsnamens als Zeichenfolge.

Schreiben Sie stattdessen Eigenschaften wie diese:

public int Foo
{
    get { return _foo; }
    set { SetValue(ref _foo, value); }  // <-- no property name here
}

Beachten Sie, dass es nicht notwendig ist, den Namen der Eigenschaft als Zeichenkette anzugeben. ActiveSharp findet das zuverlässig und korrekt selbst heraus. Es funktioniert auf der Grundlage der Tatsache, dass Ihre Eigenschaftsimplementierung das Hintergrundfeld (_foo) per ref übergibt (ActiveSharp verwendet diesen "by ref"-Aufruf, um zu erkennen, welches Hintergrundfeld übergeben wurde, und identifiziert anhand des Feldes die Eigenschaft).

2voto

Ofir Punkte 2114

Eine andere kombinierte Lösung ist die Verwendung von StackFrame:

public class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void Set<T>(ref T field, T value)
    {
        MethodBase method = new StackFrame(1).GetMethod();
        field = value;
        Raise(method.Name.Substring(4));
    }

    protected void Raise(string propertyName)
    {
        var temp = PropertyChanged;
        if (temp != null)
        {
            temp(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Verwendung:

public class TempVM : BaseViewModel
{
    private int _intP;
    public int IntP
    {
        get { return _intP; }
        set { Set<int>(ref _intP, value); }
    }
}

2 Stimmen

Ist das schnell? Ist der Zugriff auf den Stack-Frame nicht an eine bestimmte Berechtigung gebunden? Ist das im Zusammenhang mit der Verwendung von async/await robust?

0 Stimmen

@StéphaneGourichon Nein, ist es nicht. Der Zugriff auf den Stack-Frame bedeutet in den meisten Fällen einen erheblichen Leistungsabfall.

0 Stimmen

Ja, die gibt es, Sie können sie sehen unter codereview.stackexchange.com/questions/13823/

2voto

Dilshod Punkte 3127

Wenn Sie Dynamics in .NET 4.5 verwenden, müssen Sie sich keine Gedanken über INotifyPropertyChanged .

dynamic obj = new ExpandoObject();
obj.Name = "John";

wenn Name an ein Steuerelement gebunden ist, funktioniert es einwandfrei.

1 Stimmen

Gibt es Nachteile bei der Verwendung dieser Methode?

1voto

Ich weiß, dass es auf diese Frage bereits eine Unmenge von Antworten gibt, aber keine davon schien mir richtig zu sein. Mein Problem ist, dass ich keine Leistungseinbußen haben möchte und bereit bin, allein aus diesem Grund ein wenig Wortklauberei in Kauf zu nehmen. Ich auch nicht zu viel für automatische Eigenschaften entweder, die mich auf die folgende Lösung geführt:

public abstract class AbstractObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual bool SetValue<TKind>(ref TKind Source, TKind NewValue, params string[] Notify)
    {
        //Set value if the new value is different from the old
        if (!Source.Equals(NewValue))
        {
            Source = NewValue;

            //Notify all applicable properties
            foreach (var i in Notify)
                OnPropertyChanged(i);

            return true;
        }

        return false;
    }

    public AbstractObject()
    {
    }
}

Mit anderen Worten: Die obige Lösung ist praktisch, wenn es Ihnen nichts ausmacht, dies zu tun:

public class SomeObject : AbstractObject
{
    public string AnotherProperty
    {
        get
        {
            return someProperty ? "Car" : "Plane";
        }
    }

    bool someProperty = false;
    public bool SomeProperty
    {
        get
        {
            return someProperty;
        }
        set
        {
            SetValue(ref someProperty, value, "SomeProperty", "AnotherProperty");
        }
    }

    public SomeObject() : base()
    {
    }
}

Profis

  • Keine Reflexion
  • Meldet nur, wenn alter Wert != neuer Wert
  • Mehrere Objekte gleichzeitig benachrichtigen

Nachteile

  • Keine automatischen Eigenschaften (Sie können jedoch Unterstützung für beides hinzufügen!)
  • Etwas weitschweifig
  • Boxen (kleiner Leistungseinbruch?)

Aber es ist immer noch besser, als das hier zu tun,

set
{
    if (!someProperty.Equals(value))
    {
        someProperty = value;
        OnPropertyChanged("SomeProperty");
        OnPropertyChanged("AnotherProperty");
    }
}

Für jede einzelne Eigenschaft, was durch die zusätzliche Ausführlichkeit zu einem Alptraum wird ;-(

Ich behaupte nicht, dass diese Lösung leistungsfähiger ist als die anderen, sondern nur, dass sie eine praktikable Lösung für diejenigen ist, denen die anderen vorgestellten Lösungen nicht zusagen.

1voto

soi Punkte 11

Ich schlage vor, ReactiveProperty zu verwenden. Dies ist die kürzeste Methode außer Fody.

public class Data : INotifyPropertyChanged
{
    // boiler-plate
    ...
    // props
    private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, "Name"); }
    }
}

stattdessen

public class Data
{
    // Don't need boiler-plate and INotifyPropertyChanged

    // props
    public ReactiveProperty<string> Name { get; } = new ReactiveProperty<string>();
}

( DOCS )

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X