关于c#:Cruft代码

Cruft code. IoC to the rescue

关于IOC容器的实用性,获奖提交人提到,使用IOC容器,您可以:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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");
        }
    }
}

对此:

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

问题:

  • 哪个神奇的IOC容器提供了这种好处?
  • 实现这个的例子?
  • 有什么不利因素吗?
  • 在一个依赖关系复杂的项目中,当我试图将数据绑定应用到这些对象时,我会哭吗?


为了让第二个代码段正常工作,NotifyPropertyChangedWrapper当然必须使用反射(或dynamic来生成一个类,该类提供与Customer兼容的接口,并实现自动属性通知。不应该有任何数据绑定问题,但是会有一些开销。

使用动态对象的简化实现可能如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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,要么需要它的所有属性都是虚拟的。


以一种通用的方式(即实现inotifyPropertiesChanged的一段代码对于任何类)使用代理。有很多实现可以用castle.dynamicproxy或linfu或Unity来实现。这些代理库在IOC容器中有很好的支持,例如dynamicproxy与castle windsor有很好的集成,Unity Interception(或不管它叫什么)显然与Unity容器有很好的集成。


我从来没有用过它,但是你可以用postsharp创建类似的东西。


如果您正在为自动生成可绑定对象寻找特定的解决方案,那么您应该查看propertychanged.fody(以前是notifypropertyweaver)。这将重写实现inotifyPropertyChanged的类,以包括通知代码。Github页面上有一个示例。

在我看来,这比使用提议的IOC容器解决方案更整洁。但是,它是一个特定于inotifyPropertyChanged绑定的库,因此不适用于一般的解决方案,正如在链接问题中讨论的那样。