دریافت نام Property با استفاده از عبارت های Lambda در #C
دوشنبه 6 اردیبهشت 1395در این مقاله توضیح می دهیم که چگونه می توان Property ها را به عنوان پارامترهای متد از طریق عبارت های Lambda ارسال کرد و ابزاری را برای دریافت نام Property ها از طریق عبارت های Lambda ایجاد می کنیم. به عبارت دیگر نام Property ها را با استفاده از عبارت های Lambda در #C دریافت کنیم.
شما می توانید Property ها را به عنوان پارامترهای متد ارسال کنید. این کار به ویژه برای اهداف پیکربندی مناسب است. در مثال آخر ضمیمه شده به این مقاله، با ابزار PropertiesAssert عمل refactor را انجام داده ایم بنابراین شما می توانید Property هایی را به صورت lambda expression به جای متن خام تنظیم کنید.
چگونه می توانیم Property را به عنوان پارامتر Lambda ارسال کنیم؟
در ابتدا باید کدی بنویسید که Property ها را به عنوان پارامتر ارسال کند. این کمی ترسناک به نظر می رسد اما نگران نباشید:
public static string GetMemberName<T>(Expression<Func<T, object>> expression) { return GetMemberName(expression.Body); }
از طریق نوع عبارت <<Func<T, object> می توانید عبارت های lambda را برای Property ارسال کنید. T نوعی از کلاس است که Propery را نگه می دارد.
مرحله بعدی فرایند ایجاد یک متد برای دریافت نام Property از عبارت Lambda است.
ساخت Lambda Expressions Reader
شما نیاز دارید که دو متد برای یک عمل ایجاد کنید. متد اصلی شامل یک منطق برای دریافت نام Property می باشد، و متد دوم از نوع Void یا متد از نوع مقدار می باشد. اگر شما عبارت خالی یا عبارت های پشتیبانی نشده ارسال کنید یک استثنا به صورت ArgumentException رخ می دهد:
private static string GetMemberName(Expression expression) { if (expression == null) { throw new ArgumentException(expressionCannotBeNullMessage); } if (expression is MemberExpression) { // Reference type property or field var memberExpression = (MemberExpression) expression; return memberExpression.Member.Name; } if (expression is MethodCallExpression) { // Reference type method var methodCallExpression = (MethodCallExpression) expression; return methodCallExpression.Method.Name; } if (expression is UnaryExpression) { // Property, field of method returning value type var unaryExpression = (UnaryExpression) expression; return GetMemberName(unaryExpression); } throw new ArgumentException(invalidExpressionMessage); } private static string GetMemberName(UnaryExpression unaryExpression) { if (unaryExpression.Operand is MethodCallExpression) { var methodExpression = (MethodCallExpression) unaryExpression.Operand; return methodExpression.Method.Name; } return ((MemberExpression) unaryExpression.Operand).Member.Name; }
اگر این متدها به صورت extension method ایجاد شوند برای استفاده راحتتر هستند . همچنین ما یک متد اضافه کرده ایم که چندین عبارت lambda را می پذیرد. این کار از طریق عملگر param پیاده سازی شده است. برای ارسال یک متد به عنوان lambda expression از نوع <<Expression<Action<T برای پارامتر متد استفاده کنید:
public static class NameReaderExtensions { private static readonly string expressionCannotBeNullMessage = "The expression cannot be null."; private static readonly string invalidExpressionMessage = "Invalid expression."; public static string GetMemberName<T> (this T instance, Expression<Func<T, object>> expression) { return GetMemberName(expression.Body); } public static List<string> GetMemberNames<T> (this T instance, params Expression<Func<T, object>>[] expressions) { List<string> memberNames = new List<string>(); foreach (var cExpression in expressions) { memberNames.Add(GetMemberName(cExpression.Body)); } return memberNames; } public static string GetMemberName<T> (this T instance, Expression<Action<T>> expression) { return GetMemberName(expression.Body); } private static string GetMemberName(Expression expression) { if (expression == null) { throw new ArgumentException(expressionCannotBeNullMessage); } if (expression is MemberExpression) { // Reference type property or field var memberExpression = (MemberExpression) expression; return memberExpression.Member.Name; } if (expression is MethodCallExpression) { // Reference type method var methodCallExpression = (MethodCallExpression) expression; return methodCallExpression.Method.Name; } if (expression is UnaryExpression) { // Property, field of method returning value type var unaryExpression = (UnaryExpression) expression; return GetMemberName(unaryExpression); } throw new ArgumentException(invalidExpressionMessage); } private static string GetMemberName(UnaryExpression unaryExpression) { if (unaryExpression.Operand is MethodCallExpression) { var methodExpression = (MethodCallExpression) unaryExpression.Operand; return methodExpression.Method.Name; } return ((MemberExpression) unaryExpression.Operand).Member.Name; } }
تست راه اندازی ویژگی NameExtensions
Client client = new Client(); var propertyNames = client.GetMemberNames (c => c.FistName, c => c.LastName, c => c.City); foreach (var cPropertyName in propertyNames) { Console.WriteLine(cPropertyName); } string nameOfTheMethod = client.GetMemberName(c => c.ToString()); Console.WriteLine(nameOfTheMethod);
همانطور که انتظار می رود کنسول FirstName، LastName، City و ToString را نمایش داده است:
بهبود تنظیمات PropertiesAsserter با استفاده از عبارات Lambda
به طور خلاصه هدف اصلی PropertiesAsserter برای بدست آوردن تمام Property ها از یک آبجکت در برابر نسخه مورد انتظار می باشد. با این حال شما باید قادر به تنظیم آن برای پرش از برخی Property ها باشید شما ممکن است ندانید که چه مقداری مانند Property ها، فیلدهای History و غیره مورد انتظار است.
در اولین پیاده سازی این ویژگی پرش از Property ها بدست نمی آید، نام آنها به صورت string ارسال می شود:
public class ObjectToAssertValidator : PropertiesValidator<ObjectToAssertValidator, ObjectToAssert> { public void Validate(ObjectToAssert expected, ObjectToAssert actual) { this.Validate(expected, actual, "FirstName"); } }
همانطور که می دانید روش بعدی error-prompt می باشد زیرا امکان خطای تایپی و باقی ماندن Property ها وجود دارد.
متد اصلی از PropertiesAsserter می تواند برای پذیرفتن پارامترهای Property های عبارات Lambda تغییر کند. به طور داخلی این متد از ویژگی lambda expressions جدید برای بدست آوردن نام های Property ها استفاده خواهد کرد:
public void Assert<T>(T expectedObject, T realObject, params Expression<Func<T, object>>[] propertiesNotToCompareExpressions) { PropertyInfo[] properties = realObject.GetType().GetProperties(); List<string> propertiesNotToCompare = expectedObject.GetMemberNames(propertiesNotToCompareExpressions); foreach (PropertyInfo currentRealProperty in properties) { if (!propertiesNotToCompare.Contains(currentRealProperty.Name)) { PropertyInfo currentExpectedProperty = expectedObject.GetType().GetProperty(currentRealProperty.Name); string exceptionMessage = string.Format("The property {0} of class {1} was not as expected.", currentRealProperty.Name, currentRealProperty.DeclaringType.Name); if (currentRealProperty.PropertyType != typeof(DateTime) && currentRealProperty.PropertyType != typeof(DateTime?)) { MSU.Assert.AreEqual(currentExpectedProperty.GetValue(expectedObject, null), currentRealProperty.GetValue(realObject, null), exceptionMessage); } else { DateTimeAssert.AreEqual( currentExpectedProperty.GetValue(expectedObject, null) as DateTime?, currentRealProperty.GetValue(realObject, null) as DateTime?, DateTimeDeltaType.Minutes, 5); } } } }
در اینجا کد بهبود یافته تنظیمات وجود دارد:
public class ObjectToAssertAsserter : PropertiesAsserter<ObjectToAssertAsserter, ObjectToAssert> { public void Assert(ObjectToAssert expected, ObjectToAssert actual) { this.Assert(expected, actual, e => e.LastName, e => e.FirstName, e => e.PoNumber); } }
- C#.net
- 1k بازدید
- 2 تشکر