Реализация INotifyPropertyChanged - существует ли лучший способ? - PullRequest
604 голосов
/ 22 августа 2009

Microsoft должна была реализовать что-то быстрое для INotifyPropertyChanged, как в автоматических свойствах, просто укажите {get; set; notify;} Я думаю, что в этом есть смысл. Или есть какие-то осложнения для этого?

Можем ли мы сами реализовать что-то вроде «уведомить» в наших свойствах. Есть ли изящное решение для реализации INotifyPropertyChanged в вашем классе или единственный способ сделать это - вызвать событие PropertyChanged в каждом свойстве.

Если нет, то можем ли мы написать что-то для автоматической генерации кода, чтобы вызвать событие PropertyChanged?

Ответы [ 34 ]

0 голосов
/ 04 июля 2013

=> здесь мое решение со следующими функциями

 public ResourceStatus Status
 {
     get { return _status; }
     set
     {
         _status = value;
         Notify(Npcea.Status,Npcea.Comments);
     }
 }
  1. без рефлекса
  2. краткая запись
  3. нет волшебной строки в вашем бизнес-коде
  4. Возможность повторного использования PropertyChangedEventArgs в приложении
  5. Возможность уведомить несколько свойств в одном заявлении
0 голосов
/ 01 июня 2011

Я только что нашел ActiveSharp - автоматический INotifyPropertyChanged , я еще не использовал его, но выглядит хорошо.

Цитирую со своего сайта ...


Отправка уведомлений об изменении свойства без указания имени свойства как строка.

Вместо этого напишите свойства как это:

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

Обратите внимание, что нет необходимости включать имя свойства в виде строки. ActiveSharp надежно и правильно вычисляет это для себя. Он работает на основе того факта, что ваша реализация свойства передает поле поддержки (_foo) по ссылке. (ActiveSharp использует этот вызов «по ссылке», чтобы определить, какое поле поддержки было передано, и из поля оно идентифицирует свойство).

0 голосов
/ 29 октября 2018

Я пишу библиотеку, которая работает с INotifyPropertyChanged, и основная идея заключается в использовании динамического прокси для уведомления об изменениях.

репо здесь: CaulyKan / NoMorePropertyChanged

с этой библиотекой вы можете написать:

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

Затем все привязки и модификации переходят в Test1Binding, который автоматически уведомляет PropertyChange и CollectionChanged независимо от сложности TestDTO.

он также может обрабатывать зависимости.

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

Пожалуйста, дайте мне несколько советов.

0 голосов
/ 15 марта 2014

Используйте это

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

}

...