.Net лямбда-выражение - откуда появился этот параметр? - PullRequest
17 голосов
/ 30 ноября 2011

Я новичок в лямбда-выражении, поэтому, если мне не хватает важной информации в моем описании, пожалуйста, сообщите мне.Я буду держать пример как можно более простым.

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

    class SampleViewModel : ViewModelBase
{
    private ICustomerStorage storage = ModelFactory<ICustomerStorage>.Create();

    public ICustomer CurrentCustomer
    {
        get { return (ICustomer)GetValue(CurrentCustomerProperty); }
        set { SetValue(CurrentCustomerProperty, value); }
    }

    private int quantitySaved;
    public int QuantitySaved
    {
        get { return quantitySaved; }
        set
        {
            if (quantitySaved != value)
            {
                quantitySaved = value;
                NotifyPropertyChanged(p => QuantitySaved); //where does 'p' come from?
            }
        }
    }

    public static readonly DependencyProperty CurrentCustomerProperty;

    static SampleViewModel()
    {
        CurrentCustomerProperty = DependencyProperty.Register("CurrentCustomer", typeof(ICustomer),
            typeof(SampleViewModel), new UIPropertyMetadata(ModelFactory<ICustomer>.Create()));
    }
//more method definitions follow..

Обратите внимание на вызов NotifyPropertyChanged(p => QuantitySaved) чуть выше.Я не понимаю, откуда взялась буква "р".

Вот базовый класс:

  public abstract class ViewModelBase : DependencyObject, INotifyPropertyChanged, IXtremeMvvmViewModel
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void NotifyPropertyChanged<T>(Expression<Func<ViewModelBase, T>> property)
        {
            MvvmHelper.NotifyPropertyChanged(property, PropertyChanged);
        }
    }

Там есть много чего не уместного в вопросе, я уверен, но я хотел ошибиться в сторону инклюзивности.

Проблема в том, что я не понимаю, откуда берется параметр 'p', и как компилятор знает (очевидно?) Заполнить значение типа ViewModelBase с нуля?

Длязабавно, я изменил код с 'p' на 'this', так как SampleViewModel наследуется от ViewModelBase, но я столкнулся с рядом ошибок компилятора, первая из которых гласила Invalid expression term '=>' Это немного смутило меня, так как я думал, чторабота.

Кто-нибудь может объяснить, что здесь происходит?

Ответы [ 5 ]

17 голосов
/ 30 ноября 2011

откуда взято 'p' в NotifyPropertyChanged(p => QuantitySaved);

Лямбда передается методу NotifyPropertyChanged.Существует одна перегрузка этого метода.Имеет формальный тип параметра Expression<Func<ViewModelBase, T>>.То есть формальный параметр ожидает получить лямбду, которая принимает ViewModelBase и возвращает T для некоторого T.

p - это параметр, который принимает лямбда.

Компилятор может сделать вывод, что автор кода не учел явно указывать тип параметра lambda.Автор мог бы также написать:

NotifyPropertyChanged((ViewModelBase p) => QuantitySaved);

, если бы они хотели быть откровенными об этом.

как компилятор узнает, что нужно заполнить типзначение ViewModelBase с нуля?

Компилятор проверяет все возможные перегрузки NotifyPropertyChanged, которые могли бы принять лямбду в этой позиции аргумента.Он выводит формальный тип параметра лямбда из типов делегата , которые находятся в типах формальных параметров методов NotifyPropertyChanged.Пример может помочь.Предположим, у нас есть:

void M(Func<int, double> f) {}
void M(Func<string, int> f) {}

и вызов

M(x=>x.Length);

Компилятор должен определить тип лямбда-параметра x.Каковы возможности?Существует две перегрузки M. Оба принимают делегата в формальном параметре M, соответствующем первому аргументу, переданному в вызове.В первом случае функция имеет тип от int до double, поэтому x может иметь тип int.Во втором формальный параметр M - это функция от строки до int, поэтому x может быть строкой.

Теперь компилятор должен определить, какой из них правильный.Для того, чтобы первый из них был правильным, тело лямбды должно возвращать двойное число.Но если x - это int, на x нет свойства Length, которое возвращает double.Так что x не может быть int.Может ли x быть строкой?Да.Существует свойство Length для x, которое возвращает int, если x является строкой.

Поэтому компилятор определяет, что x является строкой.

Эти выводы могут усложнить экстраординарно .Несколько более сложный пример:

void M<A, B, C>(A a1, Func<List<A>, B> a2, Func<B, C> a3) {}
...
M(123, x=>x.Count.ToString(), y=>y.Length);

Вывод типа должен выводить типы A, B, C и, следовательно, типы x и y.Компилятор сначала делает вывод, что A должно быть int, поскольку a1 равно 123. Затем он делает вывод, что x должно быть List<int> из этого факта.Затем он делает вывод, что B должен быть строкой, и, следовательно, y является строкой, и, следовательно, C является типом y.Length, который является int.

Все становится намного сложнее, поверь мне.

Если вас интересует эта тема, я написал несколько статей и снял несколько видеороликов на тему различных типов вывода типов, выполняемых компилятором.См.

http://blogs.msdn.com/b/ericlippert/archive/tags/type+inference/

для всех деталей.

Для забавы я изменил код с 'p' на 'this', так как SampleViewModel наследует от ViewModelBase, но меня встретил ряд ошибок компилятора, первая из которых указала на недопустимое выражение «=>». Это немного смутило меня, так как я думал, что это сработает.сторона лямбда-оператора - это список лямбда-параметров;«this» никогда не является допустимым списком лямбда-параметров.Компилятор ожидает, что за «this» последует «.SomeMethod ()» или что-то подобное;компилятор предполагает, что за «this» никогда не последует «=>».Когда вы нарушаете это предположение, случаются плохие вещи.

10 голосов
/ 30 ноября 2011

p - это просто фиктивное имя, это имя параметра, как в любом методе. Вы можете назвать это x или Fred, если хотите.

Помните, что лямбда-выражения - это очень, очень особые анонимные методы.

В обычных методах у вас есть параметры, и у них есть имена:

public double GetQuantitysaved(ViewModelBase p) {
    return QuantitySaved;
}

В анонимных методах у вас есть параметры, и у них есть имена:

delegate(ViewModelBase p) { return QuantitySaved; }

В лямбда-выражениях у вас есть параметры, и у них есть имена:

p => QuantitySaved

p здесь играет одинаковую роль во всех трех версиях. Вы можете назвать это как хотите. Это просто имя параметра метода.

В последнем случае компилятор проделал большую работу, чтобы выяснить, что p представляет параметр типа ViewModelBase, так что p => QuantitySaved может играть роль

Expression<Func<ViewModelBase, T>> property

Ради интереса я изменил код с p на this, поскольку SampleViewModel наследуется от ViewModelBase, но я встретил серию ошибок компилятора, первая из которых гласила Invalid expression term '=>' Это сбило с толку я немного, так как я думал, что это будет работать.

Ну, this не является допустимым именем параметра, потому что это зарезервированное ключевое слово. Лучше думать о p => QuantitySaved как

delegate(ViewModelBase p) { return QuantitySaved; }

пока вы не освоитесь с идеей. В этом случае this никогда не может быть заменено на p, поскольку это недопустимое имя параметра.

8 голосов
/ 30 ноября 2011

Лямбда p => QuantitySaved является выражением типа Expression<Func<ViewModelBase, int>>.Поскольку метод NotifyPropertyChanged ищет выражение <ViewModelBase, T>, оно подходит.

Таким образом, компилятор может сделать вывод, что p является ViewModelBase.p никуда не "пришел", это в основном объявляется прямо здесь.Это параметр для лямбды.Он будет заполнен, когда кто-то использует параметр property вашего метода.Например, если вы поместите лямбду в отдельную переменную с именем lambda, вы можете вызвать ее с помощью lambda(this), и она вернет значение QuantitySaved.

Причина, по которой вы не можете использовать this в лямбде, потому что он ожидает имя параметра, а this не является допустимым именем.Дело в том, что вы можете вызывать его в любом случае ViewModelBase, а не только в том, который создал лямбду.

4 голосов
/ 30 ноября 2011

Простой способ понять это состоит в том, чтобы заменить это:

p => QuantitySaved // lambda

на это:

delegate (ViewModelBase p) { return QuantitySaved; } // anonymous delegate

Что по сути то же самое.p - это имя параметра для первого параметра вашего анонимного делегата.Вы можете дать ему любое имя, подходящее для имен параметров (this является ключевым словом, вы не можете использовать его в качестве имени параметра)

В этом конкретном примере эта p переменная избыточна, кстати, вы можете использоватьделегат без параметров.

3 голосов
/ 30 ноября 2011

Из подписи NotifyPropertyChanged:

void NotifyPropertyChanged<T>(Expression<Func<ViewModelBase, T>> property)

Метод ожидает выражение, которое принимает входные данные типа ViewModelBase и возвращает экземпляр типа T.

Параметр pявляется экземпляром ViewModelBase.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...