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

1voto

Cauly Punkte 318

Ich schreibe eine Bibliothek, die sich mit INotifyPropertyChanged, und die Hauptidee ist mit einem dynamischen Proxy, um Änderungen zu benachrichtigen.

das Repo ist hier: CaulyKan/NoMorePropertyChanged

Mit dieser Bibliothek können Sie schreiben:

    public dynamic Test1Binding { get; set; }
    public TestDTO Test1
    {
        get { return (TestDTO)Test1Binding; }
        set { SetBinding(nameof(Test1Binding), value); }
    }

Dann machen Sie alle Bindungen und Änderungen gehen zu Test1Binding, die PropertyChange und CollectionChanged automatisch benachrichtigen wird, egal wie komplex TestDTO ist.

kann es auch mit Abhängigkeiten umgehen.

    [DependsOn("Test1Binding.TestString")]
    public string Test2
    {
        get { return Test1Binding.TestString; }
    }

Bitte geben Sie mir einige Anregungen.

1voto

Tim Pohlmann Punkte 3714

Ich verwende die folgende Erweiterungsmethode (mit C# 6.0), um die INPC-Implementierung so einfach wie möglich zu gestalten:

public static bool ChangeProperty<T>(this PropertyChangedEventHandler propertyChanged, ref T field, T value, object sender,
    IEqualityComparer<T> comparer = null, [CallerMemberName] string propertyName = null)
{
    if (comparer == null)
        comparer = EqualityComparer<T>.Default;

    if (comparer.Equals(field, value))
    {
        return false;
    }
    else
    {
        field = value;
        propertyChanged?.Invoke(sender, new PropertyChangedEventArgs(propertyName));
        return true;
    }
}

Die INPC-Implementierung läuft auf Folgendes hinaus (Sie können dies entweder jedes Mal implementieren oder eine Basisklasse erstellen):

public class INPCBaseClass: INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected bool changeProperty<T>(ref T field, T value,
        IEqualityComparer<T> comparer = null, [CallerMemberName] string propertyName = null)
    {
        return PropertyChanged.ChangeProperty(ref field, value, this, comparer, propertyName);
    }
}

Dann schreiben Sie Ihre Eigenschaften wie folgt:

private string testProperty;
public string TestProperty
{
    get { return testProperty; }
    set { changeProperty(ref testProperty, value); }
}

HINWEIS: Sie können die [CallerMemberName] Erklärung in der Erweiterungsmethode, wenn Sie wollen, aber ich wollte es flexibel halten.

Wenn Sie Eigenschaften ohne ein Hintergrundfeld haben, können Sie die changeProperty :

protected bool changeProperty<T>(T property, Action<T> set, T value,
    IEqualityComparer<T> comparer = null, [CallerMemberName] string propertyName = null)
{
    bool ret = changeProperty(ref property, value, comparer, propertyName);
    if (ret)
        set(property);
    return ret;
}

Ein Anwendungsbeispiel wäre:

public string MyTestProperty
{
    get { return base.TestProperty; }
    set { changeProperty(base.TestProperty, (x) => { base.TestProperty = x; }, value); }
}

1voto

Dude505 Punkte 1

Verwenden Sie diese

using System;
using System.ComponentModel;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;

public static class ObservableFactory
{
    public static T Create<T>(T target)
    {
        if (!typeof(T).IsInterface)
            throw new ArgumentException("Target should be an interface", "target");

        var proxy = new Observable<T>(target);
        return (T)proxy.GetTransparentProxy();
    }
}

internal class Observable<T> : RealProxy, INotifyPropertyChanged, INotifyPropertyChanging
{
    private readonly T target;

    internal Observable(T target)
        : base(ImplementINotify(typeof(T)))
    {
        this.target = target;
    }

    public override IMessage Invoke(IMessage msg)
    {
        var methodCall = msg as IMethodCallMessage;

        if (methodCall != null)
        {
            return HandleMethodCall(methodCall);
        }

        return null;
    }

    public event PropertyChangingEventHandler PropertyChanging;
    public event PropertyChangedEventHandler PropertyChanged;

    IMessage HandleMethodCall(IMethodCallMessage methodCall)
    {
        var isPropertySetterCall = methodCall.MethodName.StartsWith("set_");
        var propertyName = isPropertySetterCall ? methodCall.MethodName.Substring(4) : null;

        if (isPropertySetterCall)
        {
            OnPropertyChanging(propertyName);
        }

        try
        {
            object methodCalltarget = target;

            if (methodCall.MethodName == "add_PropertyChanged" || methodCall.MethodName == "remove_PropertyChanged"||
                methodCall.MethodName == "add_PropertyChanging" || methodCall.MethodName == "remove_PropertyChanging")
            {
                methodCalltarget = this;
            }

            var result = methodCall.MethodBase.Invoke(methodCalltarget, methodCall.InArgs);

            if (isPropertySetterCall)
            {
                OnPropertyChanged(methodCall.MethodName.Substring(4));
            }

            return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
        }
        catch (TargetInvocationException invocationException)
        {
            var exception = invocationException.InnerException;
            return new ReturnMessage(exception, methodCall);
        }
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanging(string propertyName)
    {
        var handler = PropertyChanging;
        if (handler != null) handler(this, new PropertyChangingEventArgs(propertyName));
    }

    public static Type ImplementINotify(Type objectType)
    {
        var tempAssemblyName = new AssemblyName(Guid.NewGuid().ToString());

        var dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
            tempAssemblyName, AssemblyBuilderAccess.RunAndCollect);

        var moduleBuilder = dynamicAssembly.DefineDynamicModule(
            tempAssemblyName.Name,
            tempAssemblyName + ".dll");

        var typeBuilder = moduleBuilder.DefineType(
            objectType.FullName, TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract);

        typeBuilder.AddInterfaceImplementation(objectType);
        typeBuilder.AddInterfaceImplementation(typeof(INotifyPropertyChanged));
        typeBuilder.AddInterfaceImplementation(typeof(INotifyPropertyChanging));
        var newType = typeBuilder.CreateType();
        return newType;
    }
}

}

1voto

Peter Punkte 1726

Weitere Dinge, die Sie bei der Implementierung dieser Art von Eigenschaften berücksichtigen sollten, ist die Tatsache, dass die INotifyPropertyChang *ed *ing beide Ereignisargumentklassen verwenden.

Wenn Sie eine große Anzahl von Eigenschaften haben, die eingestellt werden, dann kann die Anzahl der Ereignisargument-Klasseninstanzen riesig sein, Sie sollten in Betracht ziehen, sie zwischenzuspeichern, da sie einer der Bereiche sind, in denen eine Zeichenkettenexplosion auftreten kann.

Werfen Sie einen Blick auf diese Umsetzung und die Erklärung, warum sie erdacht wurde.

Josh Smiths Blog

1voto

Jack Punkte 4364

Eine Idee, die die Reflexion nutzt:

class ViewModelBase : INotifyPropertyChanged {

    public event PropertyChangedEventHandler PropertyChanged;

    bool Notify<T>(MethodBase mb, ref T oldValue, T newValue) {

        // Get Name of Property
        string name = mb.Name.Substring(4);

        // Detect Change
        bool changed = EqualityComparer<T>.Default.Equals(oldValue, newValue);

        // Return if no change
        if (!changed) return false;

        // Update value
        oldValue = newValue;

        // Raise Event
        if (PropertyChanged != null) {
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }//if

        // Notify caller of change
        return true;

    }//method

    string name;

    public string Name {
        get { return name; }
        set {
            Notify(MethodInfo.GetCurrentMethod(), ref this.name, value);
        }
    }//method

}//class

0 Stimmen

Das ist ziemlich cool, das gefällt mir besser als der Ausdrucksansatz. Auf der Kehrseite, sollte langsamer sein.

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