2011-11-30 45 views
17

Tôi là một newbie lambda, vì vậy nếu tôi thiếu thông tin quan trọng trong mô tả của tôi, vui lòng cho tôi biết. Tôi sẽ giữ ví dụ đơn giản nhất có thể.. Biểu thức lambda net-- thông số này đến từ đâu?

Tôi sẽ xem xét mã của người khác và họ có một lớp kế thừa từ một mã khác. Đây là lớp có nguồn gốc đầu tiên, cùng với các biểu thức lambda tôi đang gặp rắc rối hiểu biết:

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.. 

Lưu ý các cuộc gọi đến NotifyPropertyChanged(p => QuantitySaved) chút ở trên. Tôi không hiểu "p" xuất phát từ đâu.

Đây là lớp cơ sở:

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); 
     } 
    } 

Có rất nhiều trong đó đó không phải Gecman cho câu hỏi Tôi chắc chắn, nhưng tôi muốn phạm sai lầm ở mặt bên của tính toàn diện.

Vấn đề là, tôi không hiểu thông số 'p' đến từ đâu, và cách trình biên dịch biết (rõ ràng?) Điền vào một giá trị kiểu của ViewModelBase từ không khí mỏng?

Để giải trí, tôi đã đổi mã từ 'p' thành 'this', vì SampleViewModel kế thừa từ ViewModelBase, nhưng tôi đã gặp phải một loạt lỗi trình biên dịch, cái đầu tiên trong số đó đã nêu là Invalid expression term '=>'. nghĩ rằng sẽ làm việc.

Có ai có thể giải thích những gì đang xảy ra ở đây không?

Trả lời

8

Lambda p => QuantitySaved là biểu thức loại Expression<Func<ViewModelBase, int>>. Kể từ khi phương pháp NotifyPropertyChanged đang tìm kiếm một biểu thức của <ViewModelBase, T>, nó phù hợp.

Vì vậy trình biên dịch có thể suy ra rằng pViewModelBase. p không "đến từ" bất cứ nơi nào, về cơ bản nó được tuyên bố ngay tại đây. Đó là một tham số cho lambda. Nó sẽ được điền vào khi ai đó sử dụng tham số property của phương thức của bạn. Ví dụ: nếu bạn đặt lambda của mình vào một biến riêng biệt có tên là lambda, bạn có thể gọi nó bằng lambda(this) và nó sẽ trả lại giá trị QuantitySaved.

Lý do bạn không thể sử dụng this trong lambda là vì nó mong đợi tên thông số và this không phải là tên hợp lệ. Vấn đề là bạn có thể gọi nó trên bất kỳ trường hợp nào của ViewModelBase, không chỉ là một trong đó tạo ra lambda.

+0

Ah, ok. Vì vậy, nó là một tuyên bố phương pháp. Tôi nghĩ đoạn trích được đề cập là - vào thời điểm đó-- gọi NotifyPropertyChanged() bằng 'p' làm tham số. Cảm ơn tất cả mọi người, đôi mắt của tôi phải mệt mỏi. – larryq

+0

Không, bạn không thể sử dụng 'this' vì đó là từ khóa dành riêng. Nó không bao giờ hợp lệ như tên tham số. – jason

+3

@larryq: Nó đang gọi NotifyPropertyChanged với một cây * biểu thức * có tham số * được gọi là p. Không có tách p từ lambda hơn bất kỳ bạn có thể tách các tuyên bố chính thức tham số 'tài sản' từ NotifyPropertyChanged. –

3

Từ chữ ký NotifyPropertyChanged:

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

Phương pháp này hy vọng một biểu thức mà phải mất một đầu vào của loại ViewModelBase và trả về một thể hiện của loại T.

Tham số p là phiên bản của ViewModelBase.

10

p chỉ là tên giả, đó là tên của tham số như trong bất kỳ phương pháp nào. Bạn có thể đặt tên cho nó là x hoặc Fred, nếu bạn muốn.

Hãy nhớ rằng, biểu thức lambda chỉ là các phương thức vô danh rất đặc biệt.

Trong phương pháp thông thường bạn có thông số, và họ có những cái tên:

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

Trong phương pháp vô danh bạn có thông số, và họ có những cái tên:

delegate(ViewModelBase p) { return QuantitySaved; } 

Trong lambda biểu thức mà bạn có tham số, và họ có tên:

p => QuantitySaved 

p đây đóng vai vai trò của tôi trong cả ba phiên bản. Bạn có thể đặt tên cho nó bất cứ điều gì bạn muốn. Nó chỉ là tên của một tham số cho phương thức.

Trong trường hợp cuối cùng, trình biên dịch thực hiện rất nhiều công việc để tìm ra rằng p đại diện cho một tham số có kiểu ViewModelBase để p => QuantitySaved có thể đóng vai trò của

Expression<Func<ViewModelBase, T>> property 

Đối với niềm vui tôi đã thay đổi mã từ p đến this, kể từ SampleViewModel được kế thừa từ ViewModelBase, nhưng tôi đã gặp phải một loạt lỗi trình biên dịch, một lỗi đầu tiên đã nêu rõ Invalid expression term '=>' Điều này làm tôi bối rối một chút vì tôi nghĩ điều đó sẽ hiệu quả.

Vâng, this không phải là tên thông số hợp lệ vì đó là từ khóa dành riêng. Tốt nhất là hãy nghĩ đến số p => QuantitySaved

delegate(ViewModelBase p) { return QuantitySaved; } 

cho đến khi bạn cảm thấy thoải mái với ý tưởng. Trong trường hợp này, this không bao giờ được thay thế cho p vì nó không phải là tên thông số hợp lệ.

4

Cách đơn giản để hiểu điều này là để thay thế này:

p => QuantitySaved // lambda 

với điều này:

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

Đó là một cách hiệu quả như vậy. p là tên thông số cho tham số đầu tiên của đại biểu ẩn danh của bạn. Bạn có thể đặt tên bất kỳ phù hợp cho tên thông số (this là từ khóa, bạn không thể sử dụng tên này làm tên thông số)

Trong ví dụ cụ thể này, biến số p này cũng có thể sử dụng đại biểu không tham số.

+0

Trong trường hợp này, nó không giống nhau, vì lambda không được chuyển đổi thành một delegate, mà là một 'Expression'. Và bạn không thể làm điều đó với các hàm ẩn danh 'delegate'. – svick

17

nơi nào 'p' đến từ trong NotifyPropertyChanged(p => QuantitySaved);

Lambda đã được thông qua với một phương pháp gọi là NotifyPropertyChanged. Có một quá tải của phương pháp đó. Nó có kiểu tham số chính thức Expression<Func<ViewModelBase, T>>. Đó là, tham số chính thức hy vọng sẽ nhận được một lambda có một ViewModelBase và trả về một T, đối với một số T.

p là tham số mà lambda nhận.

Trình biên dịch có thể suy ra rằng tác giả của mã bị bỏ quên đánh vần loại tham số lambda một cách rõ ràng. Tác giả cũng có thể viết:

NotifyPropertyChanged((ViewModelBase p) => QuantitySaved);

thăm họ muốn trở thành rõ ràng về nó.

trình biên dịch biết cách điền vào giá trị loại của ViewModelBase từ không khí mỏng như thế nào?

Trình biên dịch kiểm tra tất cả các tình trạng quá tải có thể có của NotifyPropertyChanged có thể có thể mất một lambda ở vị trí đối số đó. Nó nhập loại thông số chính thức của lambda từ đại biểu loại nằm trong các loại thông số chính thức chính thức của các phương pháp NotifyPropertyChanged. Một ví dụ có thể hữu ích. Giả sử chúng ta có:

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

và một cuộc gọi

M(x=>x.Length); 

Trình biên dịch phải suy ra kiểu của tham số lambda x. Khả năng là gì? Có hai tình trạng quá tải của M. Cả hai lấy một đại biểu trong tham số chính thức của M tương ứng với đối số đầu tiên được truyền trong cuộc gọi. Trong phần đầu tiên, hàm là từ int đến double, do đó x có thể thuộc kiểu int. Trong phần thứ hai, tham số chính thức của M là một hàm từ chuỗi thành int, vì vậy x có thể là chuỗi.

Trình biên dịch phải xác định cái nào là đúng. Để cho cái đầu tiên đúng, thân của lambda phải trả về gấp đôi. Nhưng nếu x là int, không có thuộc tính Length on x trả về giá trị gấp đôi. Vì vậy x không thể là int. Có thể x là chuỗi? Vâng. Có một thuộc tính Length on x trả về một int nếu x là chuỗi.

Do đó trình biên dịch suy ra rằng x là chuỗi.

Các khoản khấu trừ này có thể nhận được phức tạp thêm phức tạp. Ví dụ hơi phức tạp hơn:

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); 

Suy luận loại phải suy ra loại A, B, C và do đó là loại x và y. Trình biên dịch đầu tiên infers A phải được int kể từ a1 là 123. Nó sau đó infers rằng x phải là List<int> từ thực tế đó. Nó sau đó infers rằng B phải là chuỗi, và do đó y là chuỗi, và do đó C là loại y.Length, đó là int.

Nó trở nên phức tạp hơn rất nhiều từ đó, hãy tin tôi đi.

Nếu chủ đề này quan tâm đến bạn, tôi đã viết một số bài báo và quay một số video về chủ đề của các loại suy luận loại được trình biên dịch thực hiện. Xem

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

để biết tất cả chi tiết.

Đối với niềm vui tôi đã thay đổi mã từ 'p' thành 'này', vì SampleViewModel thừa hưởng từ ViewModelBase, nhưng tôi đã được đáp ứng với một loạt các lỗi biên dịch, là người đầu tiên một trong số đó statedInvalid hạn biểu hiện '=>' Điều này làm tôi bối rối một chút vì tôi nghĩ điều đó sẽ hiệu quả.

Phía bên trái chỉ chấp nhận được của toán tử lambda là danh sách tham số lambda; "this" không bao giờ là danh sách tham số lambda hợp pháp. Trình biên dịch mong đợi "này" được theo sau bởi ".SomeMethod()" hoặc một số điều như vậy; trình biên dịch giả định rằng "điều này" sẽ không bao giờ được theo sau bởi "=>". Khi bạn vi phạm giả định đó, những điều xấu xảy ra.

+0

Rất cám ơn vì lời giải thích Eric. Nhiều đánh giá cao. – larryq

Các vấn đề liên quan