Как этот код работает для обработки INotifyPropertyChanged - PullRequest
2 голосов
/ 26 июля 2011

Я вижу код ниже по этой ссылке: Элегантный способ реализации INotifyPropertyChanged

Я новичок в Expression Tree.Можете ли вы объяснить, как этот код работает просто?

спасибо

private string _name;
public string Name
{
   get { return _name; }
   set { PropertyChanged.ChangeAndNotify(ref _name, value, () => Name); }
}

public static bool ChangeAndNotify<T>(this PropertyChangedEventHandler handler,ref T field, T value, Expression<Func<T>> memberExpression)
    {
        if (memberExpression == null)
        {
            throw new ArgumentNullException("memberExpression");
        }
        var body = memberExpression.Body as MemberExpression;
        if (body == null)
        {
            throw new ArgumentException("Lambda must return a property.");
        }
        if (EqualityComparer<T>.Default.Equals(field, value))
        {
            return false;
        }

        var vmExpression = body.Expression as ConstantExpression;
        if (vmExpression != null)
        {
            LambdaExpression lambda = Expression.Lambda(vmExpression);
            Delegate vmFunc = lambda.Compile();
            object sender = vmFunc.DynamicInvoke();

            if (handler != null)
            {
                handler(sender, new PropertyChangedEventArgs(body.Member.Name));
            }
        }

        field = value;
        return true;
    }

Ответы [ 2 ]

3 голосов
/ 26 июля 2011

Дерево выражений является объектно-ориентированным представлением выражения во время выполнения. Компилятор C # может автоматически записывать ограниченное подмножество выражений в виде Expression<> экземпляров через лямбду - то есть

Func<string> anonMethod = () => "abc"; // this is a delegate
Expression<Func<string>> expression = () => "abc"; // this is an expression

Выше будет иметь ConstantExpression; ваш пример будет иметь ConstantExpression, который захватывает this, и MemberExpression, который оборачивает эту константу (this) и член (.Name). Затем во время выполнения он находит MemberExpression, который имеет MemberInfo, который имеет Name. Он может даже иметь класс захвата от ConstantExpression до , от MemberExpression до this (как поле в классе захвата) и , затем a MemberExpression до .Name.

Разница невелика, но выражение может быть разобрано и проверено во время выполнения - что очень важно для LINQ. Однако: хотя компилятор может записать их, они не могут свободно проверять; они тоже стоили.

ИМО, возможно придерживайтесь:

public string Name
{
   get { return _name; }
   set { SomeSimpleNotifyMethod(ref _name, value, this, "Name"; }
}

это проще и быстрее (очевидно, изменяя метод так, чтобы он брал строку, которую он передает событию).

0 голосов
/ 30 мая 2012

Вот метод расширения, который может определить отправителя и имя свойства, просто проверив дерево лямбда-выражений и не оказав существенного влияния на производительность:

public static class PropertyChangedExtensions
{
    public static void Raise(this PropertyChangedEventHandler handler, Expression<Func<object>> property)
    {
        if (handler == null)
            return;

        var boxingExpr = property.Body as UnaryExpression;

        var memberExpr = (MemberExpression)(boxingExpr == null ? 
            property.Body : boxingExpr.Operand);

        var propertyName = memberExpr.Member.Name;
        var sender = ((ConstantExpression)memberExpr.Expression).Value;
        handler.Invoke(sender, new PropertyChangedEventArgs(propertyName));
    }
}

и использование такое же, как у вас сейчас:

public string Name
{
    get { return name; }
    set
    {
        name = value;
        PropertyChanged.Raise(() => Name);
    }
}
...