Wybieranie elementów klasy (Lambda Expression)

System refleksji dostępny w .NET daje programiście duże możliwości operowanie strukturami klas podczas działania programu. Pobieranie informacji o poszczególnych elementach klasy wiążę się zazwyczaj z wywołaniem odpowiedniej metody i podania nazwy elementu klasy o którym informację chcielibyśmy uzyskać. Taki sposób operowania wewnętrzną strukturą programu jest mało wygodny i może powodować błędy podczas działania aplikacji.

Pewnie nie raz spotkaliście się z poniższym zapisem np. pisząc aplikację w ASP.NET MVC czy korzystając z frameworku Fluent NHibernate do definiowania mapowań dla NHibernate’a.

[csharp]
GetProperty(x => x.Name);
GetProperty(x => x.LastName);
GetProperty(x => x.Address.City);
[/csharp]

Wielu z was pewnie zastanawiało się jak zastosować taki mechanizm w pisanej bibliotece czy aplikacji. Z uwagi na to, że jakiś czas temu pisałem bibliotekę wykorzystującą taki sposób wskazywania elementów klasy postanowiłem, że opiszę pokrótce jak go zastosować. Główną zaletą stosowania takiego zapisu jest fakt, że dostajemy już na etapie kompilacji weryfikację poprawności takiego wyrażenia i automatyczne podpowiadanie składni na etapie jego tworzenia.

Żeby więcej nie przynudzać przejdę od razu do zaprezentowanie przykładu zastosowania wspomnianego mechanizmu. Zacznijmy od dwóch prostych klas których elementy będziemy wybierać:

[csharp]public enum Gender { Male, Female };

public class Person
{
public long Id { get; private set; }
public string Name { get; set; }
public string LastName { get; set; }
public string Profession { get; set; }
public Gender Gender { get; set; }
public DateTime BirthDate { get; set; }
public Address Address { get; set; }
}
[/csharp]

[csharp]public class Address
{
public string City { get; set; }
public string Street { get; set; }
public int Home { get; set; }
}
[/csharp]

Jak widzimy są to klasy reprezentujące odpowiednio osobę jak i adres. Aby móc pobierać informację o elementach klas musimy mieć narzędzie które nam to umożliwi. Do tego celu stworzymy prostą klasę generyczną która zwróci nam odpowiednie obiekty zawierające informację które chcemy uzyskać.

[csharp]
public class Info<T> where T: class
{
public PropertyInfo GetProperty(Expression<Func<T,object>> expression)
{
return ReflectionHelper.GetMemberExpression(expression.Body).Member as PropertyInfo;
}

public PropertyInfo[] GetAllPropertiesFromExpression(Expression<Func<T, object>> expression)
{
return ReflectionHelper
.GetPropertiesFromExpression(expression.Body);
}
}
[/csharp]

Jak widać powyżej klasa posiada dwie meody:

  • GetProperty – zwracająca obiekt informacyjny o właściwości wskazanej przez wyrażenie lambda
  • GetAllPropertiesFromExpression –  metoda zwracająca wszystkie właściwości w całym wyrażeniu

Obie metody jako parametr przyjmują System.Linq.Expressions.Expression<Func<T, object>>. Fakt, że w wyrażeniu operujemy delegatem funkcji daje nam to możliwość zastosowania wyrażeń lambda do ich tworzenia dzięki czemu uzyskujemy prosty zapis i podpowiadanie składni przy ich pisaniu.

Aby pobrać interesujące nas informację nie korzystamy z całości wyrażenia, a jedynie z jego części po operatorze  “=>”. Dostęp do tej części uzyskujemy poprzez właściwość Body.  Obiekt klasy System.Linq.Expressions.Expression zwracany przez właściwość Body nie posiada elementów z których możemy bezpośrednio uzyskać informację o członkach klasy które nas interesują. System.Linq.Expressions.Expression jest bazową klasą wszystkich wyrażeń i musimy ją rzutować, w naszym przypadku na obiekty klasy MemberExpression. Metoda realizująca to zadanie znajduje się poniżej.

[csharp]
public static MemberExpression GetMemberExpression(Expression expression)
{
MemberExpression memberExpression = null;
if (expression.NodeType == ExpressionType.Convert)
{
UnaryExpression body = (UnaryExpression)expression;
memberExpression = body.Operand as MemberExpression;
}
else if (expression.NodeType == ExpressionType.MemberAccess)
{
memberExpression = expression as MemberExpression;
}
if (memberExpression == null)
{
throw new ArgumentException(
String.Format("Expression {0} is not MemberExpression", expression.NodeType));
}
return memberExpression;
}
[/csharp]

W metodzie tej zawarta jest również część odpowiedzialna za rzutowanie wyrażenia na UnaryExpression i dopiero po tym uzyskujemy z niego obiekt klasy MemberExpression. Taka operacja jest wymagana aby dodać obsługę obiektów ValueType.  Bez zastosowania UnaryExpression nie moglibyśmy pobierać informacji o członkach klasy będących ValueType.

Metoda  GetMemberExpression umożliwia pobranie tylko ostatniego elementu wyrażenia. Jeżeli byśmy chcieli pobrać wszystkie właściwości z wyrażenia składającego się z kilku elementów musimy pobierać te elementy pojedynczo zaczynając od ostatniego i cofając się do elementu będącego parametrem. Metodę realizująca do zadanie widzimy poniżej.

[csharp]
public static PropertyInfo[] GetPropertiesFromExpression(Expression expression)
{
List properties = new List();
MemberExpression memberExpression = null;
Expression ex = expression;
do
{
memberExpression = ReflectionHelper.GetMemberExpression(ex);
properties.Add((PropertyInfo)memberExpression.Member);
ex = memberExpression.Expression;
}
while (ex.NodeType != ExpressionType.Parameter);

properties.Reverse();
return properties.ToArray();
}
[/csharp]

Mając gotowe narzędzia możemy je wykorzystać w testowym programie który mógłby wyglądać mniej więcej tak:

[csharp]
Console.WriteLine("Person class:");
Info personInfo = new Info();

Console.WriteLine("Get \"Name\" Property: x => x.Name");
PropertyInfo personName = personInfo.GetProperty(x => x.Name);
Console.WriteLine("Type: {0}, Property: {1}",
personName.DeclaringType.Name, personName.Name);

Console.WriteLine();
Console.WriteLine("Get all properties from expression: x => x.Address.City.Length");
var properties = personInfo.GetAllPropertiesFromExpression(x => x.Address.City.Length);
foreach (var item in properties)
{
Console.WriteLine("Type: {0}, Property: {1}",
item.DeclaringType.Name, item.Name);
}
[/csharp]

Po wykonaniu powyższego kawałku kodu na ekranie powinniśmy zobaczyć następujący efekt:

Person class:
Get "Name" Property: x => x.Name
Type: Person, Property: Name

Get all properties from expression: x => x.Address.City.Length
Type: Person, Property: Address
Type: Address, Property: City
Type: String, Property: Length

Kod zawierający program testowy wraz z wszystkimi opisanymi klasami można pobrać tutaj:
[dm]2[/dm]

487 thoughts on “Wybieranie elementów klasy (Lambda Expression)”

  1. Oh my goodness! an incredible article dude. Thanks Nonetheless I’m experiencing difficulty with ur rss . Don抰 know why Unable to subscribe to it. Is there anybody getting similar rss downside? Anyone who is aware of kindly respond. Thnkx

  2. A ѕinglе thing to aсcomplish for your animаl to hеlр him be a lot more comfy is to give him ɑn appropriate bed
    to sleep on. Drs.

  3. I would go with Birchbox if you’re more interested in skincare or are curious about sample higher priced items (but finally receiving less size for your money)
    and need to truly save up things for advantages.

  4. whoah this weblog is wonderful i love reading your articles.
    Stay up the good work! You realize, a lot of persons are searching round for
    this information, you could aid them greatly.

  5. After research a few of the blog posts in your web site now, and I truly like your means of blogging. I bookmarked it to my bookmark website listing and will be checking again soon. Pls check out my web site as well and let me know what you think.

  6. Generally I do not learn article on blogs, but I wish to
    say that this write-up very pressured me to try and do so!

    Your writing style has been amazed me. Thank you, quite nice article.

  7. The V model offers a strongly structured process similar to the waterfall model, but offers shorter
    feedback loops. When a person decorates his
    home, his goals normally reach the outdoor part of the property.

    The direction of water flow with respect to the lens – In short, the
    best shutter speed varies from one waterfall to another.

  8. This is very worthwhile, You’re a very skilled blogger.
    I have joined your feed and look forward to reading more of your superb post.
    Furthermore, I have shared your website in my social networks!

  9. This article is extremely appealing to individuals just like me.

    It’s not just thought-provoking, it draws you in right
    from the start. This is well-written articles. The views here are also appealing to me.
    Thanks.

  10. I’m usually to blogging and i actually appreciate your content. The article has actually peaks my interest. I’m going to bookmark your web site and maintain checking for new information.

  11. Hello! Someone in my Facebook group shared this site with us so I came
    to look it over. I’m definitely loving the information. I’m bookmarking and will be tweeting
    this to my followers! Terrific blog and excellent style and design.

  12. I saw your content some time back and saved it to my computer.
    Only lately have I got a opportunity to check it and I have to tell you great work.really good content,
    i actually adore this site, thanks.

  13. Extremely good post. I just stumbled upon your weblog as well as wished to say that I
    have certainly liked surfing around your blog posts.
    In any case I’ll be subscribing to your feed as well as I hope you write
    again soon!

  14. I like what you guys are publishing. Such clever work!
    carry on the outstanding works guys I’ve placed you guys to
    my blogroll. I think it will boost the value of my website.

  15. I merely desired to thank you a lot more for your remarkable website you have developed here.
    It’s full of helpfulsuggestions for those who are really interested in this kind of subject,
    primarily this post.

  16. Not often do I encounter a weblog that’s both educational and engaging, and let
    me tell you, you may have hit the nail on the head.
    Your conceptis excellent; the issue is something that
    not sufficient individuals are speaking intelligently about.
    I am very happy that I found this in my quest for something relating to
    this.

  17. Hey there! Somebody in my Facebook group distributed this website
    with us so I came to look it over. I’m surely enjoying
    the information. I’m bookmarking and will likely be tweeting this to my followers!
    Exceptional blog as well as superb style and design.

  18. Howdy are using WordPress for your site platform?
    I’m new to the blog world but I’m trying to get started and create my own. Do you require any html coding expertise to make your own blog?

    Any help would be greatly appreciated!

  19. I’ve been browsing online more than three hours today, yet I never found any interesting article like yours.
    It is pretty worth enough for me. Personally, if all website owners
    and bloggers made good content as you did, the internet will be much more useful than ever before.

  20. Thank you for some other informative blog.
    Where else may just I am getting that type of information written in such an ideal method?
    I’ve a venture that I am just now running on, and I’ve been on the look out for such information.

  21. I love your blog.. very nice colors & theme. Did you design this website yourself or did you hire someone to do it for you?

    Plz respond as I’m looking to construct my own blog and would like to find out where u got this from.
    thank you

  22. The most important thing you have to realize is not that all carrier bags are created
    equal, instead of all of these paper bags are
    similar to the kind you will get in grocery stores.

    If your child creates their own ornament or gift, that could be very special
    as well. The utility of the bag is really high, that you could use it on many
    different occasions without the regret; a Chanel Quilted bag looks good with most situations
    and everything.

  23. I together with my buddies appeared to be looking through the best procedures located on the blog and then instantly I had an awful feeling I had not thanked the website owner for those techniques. All the guys ended up passionate to read through all of them and have in effect in actuality been loving those things. Thank you for getting quite accommodating and also for obtaining such incredible areas most people are really needing to know about. My personal sincere apologies for not expressing gratitude to you sooner.

Leave a Reply