获取包含指定属性的对象实例

时间:2010-07-31 13:28:44

标签: c# extension-methods

这个问题可能需要一段时间来解释,我需要提供背景......

这只是我正在玩的东西而不是用于制作,但目前我有一些看起来像这样的代码:

var myDataModel = new DataModel();

myDataModel.PropertyChanged += myDataModel_PropertyChanged;

myDataModel.ChangeProperty(t => t.TestValue, 2);

因此,我不是直接使用myDataModel.TestValue = 2,而是使用ChangeProperty扩展方法,以便我可以处理所有更改事件并在一个地方执行任何我想要的操作。我的扩展方法看起来像这样(是的,我知道它是hacky):

public static class NotifyPropertyChangedExtensions
{
    public static void ChangeProperty<T, U>(
                                      this T instance, 
                                      Expression<Func<T, U>> propertyToChange, 
                                      U newValue)
    {
        var member = propertyToChange.Body as MemberExpression;

        if (member != null)
        {
            if (!propertyToChange.Compile().Invoke(instance).Equals(newValue))
            {
                var setProperty = instance.GetType().GetProperty(
                                      member.Member.Name, 
                                        BindingFlags.SetProperty | 
                                        BindingFlags.Public | 
                                        BindingFlags.Instance);

                if (setProperty != null)
                {
                    // actually set the property
                    setProperty.SetValue(instance, newValue, null);

                    // raise the property changed event
                    if (typeof(INotifyPropertyChanged).IsAssignableFrom(
                                                                 typeof(T)))
                    {
                        var delegatesToCall = 
                             instance.GetType().GetField("PropertyChanged", 
                                       BindingFlags.Instance | 
                                       BindingFlags.NonPublic)
                                  .GetValue(instance) as MulticastDelegate;


                        if (delegatesToCall != null)
                        {
                            var eventArgs = new PropertyChangedEventArgs(
                                                           setProperty.Name);
                            foreach (var @delegate in 
                                         delegatesToCall.GetInvocationList())
                            {
                                @delegate.Method.Invoke(
                                       @delegate.Target, 
                                       new object[] { instance, eventArgs });
                            }
                        }

                    }
                }
            }
        }
        else
        {
            throw new ArgumentException(
                    string.Format(
                           "Cannot determine the property to change {0}", 
                           propertyToChange));
        }
    }
}

使用这种架构,我的数据模型非常干净:

public class DataModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public int TestValue { get; set; }
} 

也就是说,我可以使用自动属性,而不必担心引发事件等。

现在,我真正想做的是接近这一点:

var dataModel = new DataModel();

myDataModel.PropertyChanged += myDataModel_PropertyChanged;

myDataModel.TestValue.Set(2); // this is what I want...

所以,我认为我基本上需要一个扩展方法 - 但我只能看到如何发送属性本身(本例中为TestValue)和新值。那么我想知道给定一个属性是否有可能找出它所属的类的实例?

1 个答案:

答案 0 :(得分:1)

  1. 不要这样做。它破坏了封装。

  2. 没有。在myDataModel.TestValue.Set(2);中,将始终对属性返回的值调用扩展方法。无法获取返回值的类,实例或属性。

  3. 你可以这样做:

    var t = new DataModel();
    ((Expression<Func<int>>)(() => t.Foo)).Set(100);
    

    static class Extensions
    {
        public static void Set<T>(this Expression<Func<T>> expression, T value)
        { ... }
    }
    

    但这很难看,几乎不可读,不清楚,效率低下且容易出错。

  4. 您正在寻找面向方面编程(AOP)。

    查看PostSharp或LinFu。

  5. 实现INotifyPropertyChanged还没有真正干净的解决方案。如果键入所有属性设置器太多工作或太容易出错,我会使用T4模板生成它们。