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]
This actually answered my downside, thank you!
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
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.
I am often to blogging and i really appreciate your content. The article has really peaks my interest. I am going to bookmark your site and keep checking for new information.
It’s best to take part in a contest for top-of-the-line blogs on the web. I’ll suggest this web site!
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.
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.
I’m gone to inform my little brother, that he should also
pay a visit this webpage on regular basis to take updated from
most up-to-date gossip.
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.
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.
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.
Wonderful post. Didn’t know this, thank you for
letting me know.
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!
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.
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.
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.
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.
blog.pietowski.com does it yet again! Quite a thoughtful site and a thought-provoking post. Thanks!
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!
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.
Howdy, You have performed an admirable job. I’ll surely
digg it and for my part recommend to my friends.
I’m confident they’ll be benefiting from this site.
Great post! Keep writing.
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.
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.
Digitalroom is a printing company that operates with
an automated workflow that synergizes different input of data simultaneously.
Hi! Do you use Twitter? I’d like to follow you if that would be okay.
I’m undoubtedly enjoying your blog and look forward
to new updates.
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.
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!
you’ve an ideal blog here! would you prefer to make some invite posts on my weblog?
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.
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.
WONDERFUL Post.thanks for share..extra wait .. ?
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
What’s up, after reading this remarkable post i am too delighted
to share my familiarity here with colleagues.
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.
Today, while I was at work, my sister stole my iphone and tested to see if it can survive a 25 foot drop, just so she can be a youtube sensation. My iPad
is now broken and she has 83 views. I know this is completely off topic but I had to
share it with someone!
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.