2011-11-30 55 views
17

我是拉姆達的新手,所以如果我在描述中遺漏了重要信息,請告訴我。我會盡可能保持簡單。.net lambda表達式 - 此參數來自哪裏?

我正在檢查別人的代碼,他們有一個類從另一個繼承。這裏的派生類第一,與我唔明lambda表達式一起:以上

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)位。我不明白「p」是從哪裏來的。

這裏的基類:

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」到「這個」,因爲SampleViewModel從ViewModelBase繼承,但我遇到了一系列的編譯器錯誤的,其中第一個陳述的Invalid expression term '=>'這讓我感到困惑了一下,因爲我認爲這將工作。

任何人都可以解釋這裏發生了什麼?

回答

8

lambda p => QuantitySavedExpression<Func<ViewModelBase, int>>類型的表達式。由於方法NotifyPropertyChanged正在尋找<ViewModelBase, T>的表達式,因此它很合適。

所以編譯器能夠推斷出pViewModelBasep沒有「來自」任何地方,它基本上是在這裏宣佈。這是lambda的參數。當有人使用您的方法的參數property時,它將被填充。例如,如果您將lambda放入一個名爲lambda的單獨變量中,則可以使用lambda(this)來調用它,並返回QuantitySaved的值。

您不能在lambda中使用this的原因是因爲它期待參數名稱,並且this不是有效的名稱。關鍵是你可以在ViewModelBase的任何實例上調用它,而不僅僅是創建lambda的實例。

+0

啊,好的。所以這是一個方法聲明。我認爲有問題的片段是 - 在那一刻 - 用'p'作爲參數調用NotifyPropertyChanged()。謝謝大家,我的眼睛一定很累。 – larryq

+0

不,你不能使用'this',因爲它是一個保留關鍵字。它永遠不會作爲參數名稱有效。 – jason

+3

@larryq:它調用NotifyPropertyChanged,它帶有*表達式樹*,它有一個*參數*,稱爲p。除了可以從NotifyPropertyChanged中分離形式參數「屬性」的聲明外,沒有任何辦法可以將lambda與p分開。 –

3

從NotifyPropertyChanged簽名:

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

的方法,預計需要ViewModelBase類型的輸入,並返回T類型的實例的表達式。

p參數是ViewModelBase的一個實例。

10

p僅僅是一個虛擬的名字,就像在任何方法參數的名稱。如果您願意,您可以將其命名爲xFred

記住,lambda表達式只是非常,非常特別的匿名方法。

在你有參數常規方法,他們有名稱:

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

在你有參數的匿名方法,而且他們有名字:您有參數

delegate(ViewModelBase p) { return QuantitySaved; } 

在lambda表達式,並他們有這樣的名字:

p => QuantitySaved 

這個p這裏玩sa我在所有三個版本中扮演角色。你可以任意命名。這只是方法參數的名稱。

在最後一種情況下,編譯器做了很多工作來弄清楚,p代表ViewModelBase類型的參數,以便p => QuantitySaved可以起到

Expression<Func<ViewModelBase, T>> property 

角色爲了好玩,我改變了代碼從pthis,因爲從ViewModelBaseSampleViewModel繼承,但我遇到了一系列的編譯器錯誤的,其中第一個表示Invalid expression term '=>'這讓我感到困惑了一下,因爲我認爲這是可行的。

那麼,this不是一個有效的參數名稱,因爲它是一個保留關鍵字。這是最好的認爲p => QuantitySaved

delegate(ViewModelBase p) { return QuantitySaved; } 

,直到你得到舒服的想法。在這種情況下,this永遠不能替代p,因爲它不是有效的參數名稱。

4

最簡單的方式來理解這是爲了替換此:

p => QuantitySaved // lambda 

與此:

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

這實際上是一樣的。 p是匿名代理的第一個參數的參數名稱。你可以給它任何名稱適合參數名稱(this是一個關鍵字,你不能用它作爲參數名)

在這個特殊的例子這p變量的方式冗餘,可以使用參數的委託爲好。

+0

在這種情況下,它不一樣,因爲lambda沒有轉換爲委託,而是一個'Expression'。你不能用'delegate'匿名函數來做到這一點。 – svick

17

哪裏 'P' 來自於 NotifyPropertyChanged(p => QuantitySaved);

拉姆達被傳遞到一個叫做NotifyPropertyChanged方法。該方法有一個超載。它有正式參數類型Expression<Func<ViewModelBase, T>>。也就是說,形式參數希望得到一個帶有ViewModelBase並返回一個T的lambda,對於某些T.

p是,拉姆達需要的參數。

編譯器能夠推斷代碼的作者忽略了明確地拼出拉姆達參數的類型。筆者還可以這樣寫:

NotifyPropertyChanged((ViewModelBase p) => QuantitySaved);

原本他們想成爲明確一下。

編譯器如何知道從簡單的空氣中填充ViewModelBase的類型值?

編譯器檢查NotifyPropertyChanged的所有可能的重載,它們可能會在該參數位置採用lambda表達式。它推斷從委託類型,可在形式參數類型的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可以是字符串。

編譯器現在必須確定哪一個是正確的。爲了讓第一個正確,lambda的主體必須返回一個double。但是,如果x是int,那麼返回double的x上沒有屬性Length。所以x不能是int。 x可以是字符串嗎?是。 x上有一個屬性Length,如果x是字符串,則返回int。

因此,編譯器推斷出x是字符串。

這些扣除可以獲得extraoridinarily複雜。稍微更復雜的例子:

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」到「這個」,由於SampleViewModel從ViewModelBase繼承,但我遇到了一系列編譯器錯誤的,其中的第一個的statedInvalid表達術語「=>」這讓我感到困惑,因爲我認爲這會起作用。

lambda操作符的唯一可接受的左手邊是lambda參數列表; 「this」從來不是合法的lambda參數列表。編譯器期望「this」後面跟着「.SomeMethod()」或其他某些東西;編譯器假定「this」後面不會跟「=>」。當你違反這個假設時,壞事發生。

+0

非常感謝Eric的解釋。非常感激。 – larryq