Крутой код. IoC на помощь

В question о полезности контейнера IoC, победитель-заявитель отметил, что с контейнером IoC вы можете принять это:

public class UglyCustomer : INotifyPropertyChanged
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            string oldValue = _firstName;
            _firstName = value;
            if(oldValue != value)
                OnPropertyChanged("FirstName");
        }
    }

    private string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set
        {
            string oldValue = value;
            _lastName = value;
            if(oldValue != value)
                OnPropertyChanged("LastName");
        }
    }
}

:

var bindingFriendlyInstance = IoC.Resolve<Customer>(new NotifyPropertyChangedWrapper()); 

Вопросы:

  • Какой волшебный контейнер IoC обеспечивает эту доброту?
  • Пример реализации этого?
  • Любые недостатки?
  • В проекте со сложными зависимостями я буду плакать, когда пытаюсь применить привязку данных к этим объектам?

Ответ 1

Для того чтобы ваш второй фрагмент кода работал, NotifyPropertyChangedWrapper, безусловно, пришлось бы использовать отражение (или dynamic) для создания класса, который обеспечивает интерфейс, совместимый с Customer, и реализует автоматическое уведомление о свойствах. Не должно быть никаких проблем с привязкой данных, но будет немного накладных расходов.

Упрощенная реализация, использующая динамический объект, может выглядеть примерно так:

public class NotifyPropertyChangedWrapper<T> 
    : DynamicObject, INotifyPropertyChanged
{
    private T _obj;

    public NotifyPropertyChangedWrapper(T obj)
    {
        _obj = obj;
    }

    public override bool TryGetMember(
        GetMemberBinder binder, out object result)
    {
        result = typeof(T).GetProperty(binder.Name).GetValue(_obj);
        return true;
    }

    // If you try to set a value of a property that is
    // not defined in the class, this method is called.
    public override bool TrySetMember(
        SetMemberBinder binder, object value)
    {
        typeof(T).GetProperty(binder.Name).SetValue(_obj, value);
        OnPropertyChanged(binder.Name);
        return true;
    }

    // Implement OnPropertyChanged...
}

Очевидно, что любой код, который потребляет один из этих объектов, потеряет любую статическую безопасность. Другой вариант - создать класс, реализующий тот же интерфейс, что и обернутый класс. В Интернете есть много примеров. Главное требование состоит в том, чтобы ваш Customer должен был либо быть interface, либо потребовать, чтобы все его свойства были виртуальными.

Ответ 2

Сделать это в общем виде (т.е. один фрагмент кода, реализующий INotifyPropertyChanged для любого класса) используйте прокси. Для этого существует множество реализаций для Castle.DynamicProxy или LinFu или Unity. Эти прокси-библиотеки имеют хорошую поддержку в контейнерах IoC, например, DynamicProxy имеет хорошую интеграцию с перехватом Castle Windsor и Unity (или тем, что он назвал) имеет, очевидно, хорошую интеграцию с контейнером Unity.

Ответ 3

Я никогда не использовал его, но вы можете создать что-то вроде этого, используя PostSharp.

Ответ 4

Если вы ищете конкретное решение для автоматического создания связанных объектов, вы должны посмотреть PropertyChanged.Fody (ранее NotifyPropertyWeaver). Это перезаписывает классы, реализующие INotifyPropertyChanged, чтобы включить код уведомления. На странице github есть пример.

По-моему, это более аккуратно, чем использование предлагаемого контейнерного решения IOC. Тем не менее, это библиотека, специфичная для привязки INotifyPropertyChanged, поэтому не применима в качестве общего решения, как обсуждалось в вашем связанном вопросе.