废弃代码。IoC 来拯救

22

在有关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容器提供这种好处?
  • 有一个实现这个的例子吗?
  • 有什么缺点吗?
  • 在一个具有复杂依赖关系的项目中,当我尝试将数据绑定到这些对象时,我会哭泣吗?

2
我相信答案只是在玩弄一个“想法”,而不是实际的实现。 - Tim Lloyd
1
@chibacity。在这种情况下,Ben Scheirman比我见过的任何妓女都更会挑逗人(当然是在电影中)。 - AngryHacker
如果你需要一个没有逻辑而纯粹是DTO和属性更改通知的客户端,最好将其声明为接口ICustomer。创建一个具有动态代码生成的具体类并不是什么大问题。 - Pauli Østerø
4个回答

9
要使您的第二个代码片段工作,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必须是一个接口或者它需要所有属性都是虚拟的。


是的,如果你需要一个没有逻辑但纯DTO和属性更改通知的客户端,最好将其声明为接口ICustomer,然后这样的构造并不难创建一个具有动态代码生成的具体类。 - Pauli Østerø
有没有不用 DynamicObject 的方法?我使用的是 .NET 3.5。而且不想放弃智能感知。 - AngryHacker
你需要按照Pauli所说的去做。我以前做过这个,但代码很长,而且我现在没有它。这里有一篇文章,别人在这里做了同样的事情:http://grahammurray.wordpress.com/2010/04/13/dynamically-generating-types-to-implement-inotifypropertychanged/ - Jacob
@AngryHacker,下面Mauricio的答案使用DynamicProxy是一个相当不错的选择。DynamicProxy本身就是使用.Net代理(TransparentProxy + RealProxy)和MarshalByRefObject的另一种选择。 - Tim Lovell-Smith

3
如果您正在寻找一种自动生成可绑定对象的特定解决方案,则应查看PropertyChanged.Fody(以前称为NotifyPropertyWeaver)。这个库可以重写实现了INotifyPropertyChanged接口的类以包括通知代码。该项目主页上有一个示例。
在我看来,这比使用提议的IOC容器解决方案更简洁。但是,它只适用于INotifyPropertyChanged绑定,因此不能作为通用解决方案,正如您所链接的问题所讨论的那样。

这个问题特别询问提供该功能的哪个IOC容器。不要打开一个已经有接受答案的两年前的问题,并回答与问题无关的答案。 - jgauffin
2
我理解这个问题与减少代码冗余有关,我从那个角度回答。我认为IOC的重点主要是因为它受到了另一个IOC问题的启发。即使我的答案没有使用IOC,我认为指出其他技术可能更适合实现所需的结果也是有价值的。此外,元站上的这个问题得出结论,回答旧问题是可以的。 - David Turvey

3
为了以通用的方式实现INotifyPropertyChanged(即一段代码为任何类实现INotifyPropertyChanged),使用代理。有许多实现可使用Castle.DynamicProxyLinFuUnity等代理库进行此操作。这些代理库在IoC容器中得到了很好的支持,例如DynamicProxy已经和Castle Windsor集成良好,而Unity Interception(或称其他内容)显然与Unity容器集成良好。

2

我从未使用过它,但据说你可以使用PostSharp创建类似于这样的东西。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接