[英].Net lambda expression— where did this parameter come from?
我是一個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'更改為'this',因為SampleViewModel繼承自ViewModelBase,但我遇到了一系列編譯錯誤,其中第一個Invalid expression term '=>'
這讓我有點困惑因為我認為這會奏效。
誰能解釋一下這里發生了什么?
'p'來自
NotifyPropertyChanged(p => QuantitySaved);
lambda被傳遞給一個名為NotifyPropertyChanged
的方法。 該方法有一個重載。 它具有形式參數類型Expression<Func<ViewModelBase, T>>
。 也就是說,形式參數期望得到一個帶有ViewModelBase的lambda並返回一個T.
p
是lambda采用的參數。
編譯器能夠推斷出代碼的作者忽略了明確地說明lambda參數的類型。 作者也可以寫:
NotifyPropertyChanged((ViewModelBase p) => QuantitySaved);
他們想要明確它嗎?
編譯器如何知道從空中填充ViewModelBase的類型值?
編譯器會檢查NotifyPropertyChanged
所有可能重載,這些重載可能會在該參數位置中占用lambda。 它從NotifyPropertyChanged
方法的形式參數類型中的 委托類型推斷出lambda的形式參數類型 。 一個例子可能有幫助。 假設我們有:
void M(Func<int, double> f) {}
void M(Func<string, int> f) {}
和一個電話
M(x=>x.Length);
編譯器必須推斷lambda參數x的類型。 有什么可能性? M有兩個重載。兩個都在M的形式參數中占用一個委托,該委托對應於在調用中傳遞的第一個參數。 在第一個函數中,函數是從int到double,因此x可以是int類型。 在第二個中,M的形式參數是從string到int的函數,因此x可以是string。
編譯器現在必須確定哪一個是正確的。 為了使第一個正確,lambda的主體必須返回一個雙精度。 但是如果x是int,則x上沒有返回double的屬性Length。 所以x不能是int。 x可以是字符串嗎? 是。 如果x是字符串,則x上有一個屬性Length,它返回一個int。
因此編譯器推斷出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,但我遇到了一系列編譯器錯誤,其中第一個聲明了無效的表達式術語'=>'這讓我感到很困惑我認為這會奏效。
lambda運算符唯一可接受的左側是lambda參數列表; “this”永遠不是合法的lambda參數列表。 編譯器期望“this”后面跟着“.SomeMethod()”或者其他東西; 編譯器假定“this”永遠不會跟“=>”。 當你違反這個假設時,會發生壞事。
p
只是一個虛擬名稱,它是任何方法中的參數名稱。 如果你願意,可以將它命名為x
或Fred
。
請記住,lambda表達式只是非常非常特殊的匿名方法。
在常規方法中,您有參數,並且它們具有名稱:
public double GetQuantitysaved(ViewModelBase p) {
return QuantitySaved;
}
在匿名方法中,您有參數,並且它們具有名稱:
delegate(ViewModelBase p) { return QuantitySaved; }
在lambda表達式中,您有參數,並且它們具有名稱:
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
因為它不是有效的參數名稱。
lambda p => QuantitySaved
是Expression<Func<ViewModelBase, int>>
類型的Expression<Func<ViewModelBase, int>>
。 由於NotifyPropertyChanged
方法正在尋找<ViewModelBase, T>
的表達式,因此它適合。
因此編譯器能夠推斷出p
是ViewModelBase
。 p
並非“來自”任何地方,它基本上都是在這里宣布的。 它是lambda的參數。 當有人使用您方法的property
參數時,它將被填充。 例如,如果將lambda放入一個名為lambda
的單獨變量中,則可以使用lambda(this)
調用它,並返回QuantitySaved
值。
你不能使用的原因, this
在lambda是因為它需要一個參數的名稱, this
是不是一個有效的名稱。 關鍵是你可以在任何ViewModelBase
實例上調用它,而不僅僅是創建lambda的實例。
理解這一點的簡單方法是替換它:
p => QuantitySaved // lambda
有了這個:
delegate (ViewModelBase p) { return QuantitySaved; } // anonymous delegate
這實際上是一樣的。 p
是匿名委托的第一個參數的參數名稱。 您可以為其指定任何適合參數名稱的名稱( this
是一個關鍵字,您不能將其用作參數名稱)
在這個特定的例子中,這個p
變量是冗余的,你可以使用無參數委托。
從NotifyPropertyChanged簽名:
void NotifyPropertyChanged<T>(Expression<Func<ViewModelBase, T>> property)
該方法需要一個表達式,該表達式接受ViewModelBase
類型的輸入並返回類型為T
的實例。
p參數是ViewModelBase的一個實例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.