در مثالهای زیر مجموعهای از Reflectionهای ساده و کاملا کاربردی است که من با آنها روبرو شده ام.
کوتاه سازی کدهای نمایش یک View در ASP.NET MVC با Reflection
یکی از قسمتهایی که مرتبا با آن سر و کار دارید، نمایش اطلاعات است. حتی یک جدول را هم که میخواهید بسازید، باید ستونهای آن جدول را یک به یک معرفی کنید. ولی در عمل، یک Reflection ساده این کار به یک تابع چند خطی و سپس برای ترسیم هر ستون جدول از دو خط استفاده خواهید کرد ولی مزیتی که دارد این است که این تابع برای تمامی جدولها کاربردی عمومی پیدا میکند. برای نمونه دوست داشتم برای بخش مدیر، قسمت پروفایلی را ایجاد کنم و در آن اطلاعاتی چون نام، نام خانوادگی، تاریخ تولد، تاریخ ایجاد و خیلی از اطلاعات دیگر را نمایش دهم. به جای اینکه بیایم برای هر قسمت یک خط partial ایجاد کنم، با استفاده از reflection و یک حلقه، تمامی اطلاعات را به آن پارشال پاس میکنم. مزیت این روش این است که اگر بخواهم در یک جای دیگر، اطلاعات یک محصول یا یک فاکتور را هم نمایش دهم، باز هم همین تابع برایم کاربرد خواهد داشت:
تصویر زیر را که برگرفته از یک قالب Bootstrap است، ملاحظه کنید. اصلا علاقه ندارم که برای یک به یک آنها، یک سطر جدید را تعریف کنم و به View بگویم این پراپرتی را نشان بده؛ دوباره مورد بعدی هم به همین صورت و دوباره و دوباره و ... . دوست دارم یک تابع عمومی، همهی این کارها را خودکار انجام دهد.
ساختار اطلاعاتی تصویر فوق به شرح زیر است:
<div> <div> <div> <p><span>First Name </span>: Jonathan</p> </div> </div> </div>
BioRow_
@model System.Web.UI.WebControls.ListItem <div> <p><span>@Model.Text </span>: @Model.Value</p> </div>
@using System.Web.UI.WebControls @using ZekrWepApp.Filters @model ZekrModel.Admin <div> <h1>Bio Graph</h1> <div> @{ ListItemCollection collection = GetCustomProperties.Get(Model,exclude:new string[]{"Poems","Id"}); foreach (var item in collection) { Html.RenderPartial(MVC.Shared.Views._BioRow, item); } } </div> </div>
کد درون این کلاس ایستا را بررسی میکنیم؛ این کلاس دو متد دارد یکی عمومی و دیگری خصوصی است:
public class GetCustomProperties { private static PropertyInfo[] getObjectsInfos(object obj,string[] inclue,string[] exclude ) { var list = obj.GetType().GetProperties(); PropertyInfo[] outputPropertyInfos = null; if (inclue != null) { return list.Where(propertyInfo => inclue.Contains(propertyInfo.Name)).ToArray(); } if (exclude != null) { return list.Where(propertyInfo => !exclude.Contains(propertyInfo.Name)).ToArray(); } return list; } }
متد عمومی که در این کلاس قرار دارد به شرح زیر است:
public static ListItemCollection Get(object obj,string[] inclue=null,string[] exclude=null) { var propertyInfos = getObjectsInfos(obj, inclue, exclude); if (propertyInfos == null) throw new ArgumentNullException("propertyInfos is null"); var collection = new ListItemCollection(); foreach (PropertyInfo propertyInfo in propertyInfos) { string name = propertyInfo.Name; foreach (Attribute attribute in propertyInfo.GetCustomAttributes(true)) { DisplayAttribute displayAttribute = attribute as DisplayAttribute; if (displayAttribute != null) { name = displayAttribute.Name; break; } } string value = ""; object objvalue = propertyInfo.GetValue(obj); if (objvalue != null) value = objvalue.ToString(); collection.Add(new ListItem(name,value)); } return collection; }
کد بالا پراپرتیها را دریافت و یک به یک متادیتاهای آن را بررسی کرده و در صورتی که از متادیتای Display استفاده کرده باشند، مقدار آن را جایگزین نام پراپرتی خواهد کرد. در مورد مقدار هم از آنجا که اگر پراپرتی با Null پر شده باشد، تبدیل به رشتهای با پیام خطای روبرو خواهد شد. در نتیجه بهتر است یک شرط احتیاط هم روی آن پیاده شود. در آخر هم از متن و مقدار، یک آیتم ساخته و درون Collection اضافه میکنیم و بعد از اینکه همه پراپرتیها بررسی شدند، Collection را بر میگردانیم.
[Display(Name = "نام کاربری")] public string UserName { get; set; }
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Web; using System.Web.Mvc.Html; using System.Web.UI.WebControls; using Links; namespace ZekrWepApp.Filters { public class GetCustomProperties { public static ListItemCollection Get(object obj,string[] inclue=null,string[] exclude=null) { var propertyInfos = getObjectsInfos(obj, inclue, exclude); if (propertyInfos == null) throw new ArgumentNullException("propertyInfos is null"); var collection = new ListItemCollection(); foreach (PropertyInfo propertyInfo in propertyInfos) { string name = propertyInfo.Name; foreach (Attribute attribute in propertyInfo.GetCustomAttributes(true)) { DisplayAttribute displayAttribute = attribute as DisplayAttribute; if (displayAttribute != null) { name = displayAttribute.Name; break; } } string value = ""; object objvalue = propertyInfo.GetValue(obj); if (objvalue != null) value = objvalue.ToString(); collection.Add(new ListItem(name,value)); } return collection; } private static PropertyInfo[] getObjectsInfos(object obj,string[] include,string[] exclude ) { var list = obj.GetType().GetProperties(); PropertyInfo[] outputPropertyInfos = null; if (include != null) { return list.Where(propertyInfo => include.Contains(propertyInfo.Name)).ToArray(); } if (exclude != null) { return list.Where(propertyInfo => !exclude.Contains(propertyInfo.Name)).ToArray(); } return list; } } }
لیستی از پارامترها با Reflection
مورد بعدی که سادهتر بوده و از کد بالا مختصرتر هم هست، این است که قرار بود برای یک درگاه، یک سری اطلاعات را با متد Post ارسال کنم که نحوهی ارسال اطلاعات به شکل زیر بود:
amount=1000&orderId=452&Pid=xxx&....
using System; using System.Collections.Generic; using System.Linq; namespace Utils { public class QueryStringParametersList { private string Symbol = "&"; private List<KeyValuePair<string, string>> list { get; set; } public QueryStringParametersList() { list = new List<KeyValuePair<string, string>>(); } public QueryStringParametersList(string symbol) { Symbol = symbol; list = new List<KeyValuePair<string, string>>(); } public int Size { get { return list.Count; } } public void Add(string key, string value) { list.Add(new KeyValuePair<string, string>(key, value)); } public string GetQueryStringPostfix() { return string.Join(Symbol, list.Select(p => Uri.EscapeDataString(p.Key) + "=" + Uri.EscapeDataString(p.Value))); } } }
یک متغیر به نام symbol دارد و در صورتی در شرایط متفاوت، قصد چسپاندن چیزی را به یکدیگر با علامتی خاص داشته باشید، این تابع میتواند کاربرد داشته باشد. این متد از یک لیست کلید و مقدار استفاده کرده و پارامترهایی را که به آن پاس میشود، نگهداری و سپس توسط متد GetQueryStringPostfix آنها را با یکدیگر الحاق کرده و در قالب یک رشته بر میگرداند.
کاربرد Reflection در اینجا این است که من باید دوبار به شکل زیر، دو نوع اطلاعات متفاوت را پست کنم. یکی موقع ارسال به درگاه و دیگری موقع بازگشت از درگاه.
QueryStringParametersList queryparamsList = new QueryStringParametersList(); ueryparamsList.Add("consumer_key", requestPayment.Consumer_Key); queryparamsList.Add("amount", requestPayment.Amount.ToString()); queryparamsList.Add("callback", requestPayment.Callback); queryparamsList.Add("description", requestPayment.Description); queryparamsList.Add("email", requestPayment.Email); queryparamsList.Add("mobile", requestPayment.Mobile); queryparamsList.Add("name", requestPayment.Name); queryparamsList.Add("irderid", requestPayment.OrderId.ToString());
ولی با استفاده از کد Reflection که در بالاتر عنوان شد، باید نام و مقدار پراپرتی را گرفته و در یک حلقه آنها را اضافه کنیم، بدین شکل:
private QueryStringParametersList ReadParams(object obj) { PropertyInfo[] propertyInfos = obj.GetType().GetProperties(); QueryStringParametersList queryparamsList = new QueryStringParametersList(); for (int i = 0; i < propertyInfos.Count(); i++) { queryparamsList.Add(propertyInfos[i].Name.ToLower(),propertyInfos[i].GetValue(obj).ToString() ); } return queryparamsList; }