Κάθε προσθήκη στη γλώσσα έχει το δικό της κόστος (περιπλοκότητα, "ρύπανση", ταχύτητα). Προφανώς ο Hejlsberg και η ομάδα του θεώρησαν ότι το κόστος της προσθήκης π.χ. του Title.Property ή Description.Member θα είχε σχετικά περιορισμένη χρήση ενώ μπορούσε να καλυφθεί από άλλες μεθόδους.
Τέτοια σύνταξη υπάρχει σε αρκετές δυναμικές γλώσσες, και στην ουσία εκτελούν ένα function που σου επιστρέφει την πληροφορία που θέλεις. Εκεί όμως αυτή η δυνατότητα υπάρχει εξαρχής και χρησιμοποιεί μία υποδομή που επιτρέπει και επιταχύνει αυτό τον τρόπο κλήσης. Στην περίπτωση της C# όμως, θα έπρεπε ο compiler να "μαντέψει" ουσιαστικά σε ποιά περίπτωση θέλεις να καλέσεις ένα property και σε ποιά να το περάσεις ως member. Επιπλέον, ο compiler θα έπρεπε να αποφύγει τη δημιουργία κώδικα για reflection γιατί είναι πολύ πιο αργό.
Με το lambda μπορώ να πάρω πληροφορίες για το property που έδωσα χωρίς να χρησιμοποιήσω reflection. Στην C#, ένα lambda μπορείς άνετα να το δεις ως Expression χωρίς να το εκτελέσεις και να δεις από τί αποτελείται. Για παράδειγμα, το signature της Load δέχεται Expression<Func<T,Object>>. Χωρίς να κοιτάξω τον κώδικα της με το Reflector ή κάτι παρόμοιο, βλέπω ότι μπορώ να φτιάξω την παρακάτω μέθοδο:
private void MyLoad<TRoot>(TRoot root, Expression<Func<TRoot, object>> expression)
{
var member = ((expression.Body as LambdaExpression).Body as MemberExpression).Member;
var memberName = member.Name;
var myFunction = expression.Compile();
var value=myFunction(root);
}
Όπως βλέπεις, από την παραπάνω σύνταξη μπορώ να πάρω τόσο το ίδιο το property (member), όσο και ένα function (myFunction) το οποίο θα καλέσει το property επάνω σε όποιο αντικείμενο του περάσω. Όταν έρθει η ώρα μπορώ να καλέσω αυτό το function. Στην πραγματικότητα, σε κανένα σημείο δεν έχω χρησιμοποιήσει Reflection, ούτε καν όταν βρίσκω το όνομα του Property γιατί αυτές οι κλάσεις δημιουργήθηκαν όταν έγινε compile το lambda. Εγώ απλά έκανα τα casts για να πάρω την πληροφορία που ήθελα στο τέλος.
Προφανώς, έχει μεγάλη σημασία το τί θέλει να κάνει το function. Για παράδειγμα, η MyLoad μου επιτρέπει και να βρω το όνομα του property αλλά και να εκτελέσω το lambda δίνοντας όποιο root αντικείμενο θέλω. Η NotifyPropertyChange που είχα δείξει νωρίτερα από την άλλη, δουλεύει μόνο με τα properties του αντικειμένου στο οποίο έχει οριστεί:
public virtual void NotifyOfPropertyChange<TProperty>(Expression<Func<TProperty>> property)
{
RaisePropertyChanged(property.GetMemberInfo().Name);
}
Και το καλώ ως NotifyOfPropertyChange (()=>MyProperty).
Τέλος, χωρίς να αλλάξω τον τρόπο κλήσης μπορώ να αλλάξω το τί κανεί το function αλλάζοντας το signature από Expression<Func<T>> σε Func<T>. Σε ένα τέτοιο function μπορώ να καλέσω απευθείας το function ή να το κρατήσω για αργότερα, αλλά δεν θα μπορούσα να βρω το όνομα του. Παρόλα αυτά, η σύνταξη παραμένει η ίδια, π.χ. GetTheValueLater(()=>MyProperty).
Όπως βλέπεις, το lambda επιτρέπει να κάνω πολύ περισσότερα πράγματα χωρίς να δυσκολεύει ιδιαίτερα χωρίς κάποια ιδιαίτερη επιβάρυνση.
Παναγιώτης Καναβός, Freelancer
Twitter: http://www.twitter.com/pkanavos