روشهایی برای بهینهسازی استفاده از Reflection در #C
نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۱/۱۵ ۲۱:۴۹
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
System.Reflection در دسترس قرار دارد و به برنامهنویسان اجازه میدهد تا بدون دانستن ساختار دقیق یک شیء در زمان کامپایل (Compile-Time)، به آن دسترسی پیدا کنند. برای مثال، میتوان نام یک کلاس، لیست متدهای آن، یا حتی مقادیر خصوصیاتش را در زمان اجرا استخراج کرد.MethodInfo است. بنابراین، بهینهسازی استفاده از آن برای برنامههایی که به عملکرد بالا نیاز دارند، ضروری است.MethodInfo را دوباره استخراج کنید، میتوانید آن را یکبار محاسبه و در یک متغیر استاتیک ذخیره کنید. مثال زیر این مفهوم را نشان میدهد:public class Person
{
public int Age { get; set; }
public void SetAge(int age) => Age = age;
}
public class ReflectionExample
{
private static readonly MethodInfo _setAgeMethod =
typeof(Person).GetMethod("SetAge", new[] { typeof(int) });
public void SetAgeWithReflection(Person person, int age)
{
_setAgeMethod.Invoke(person, new object[] { age });
}
}MethodInfo یکبار در متغیر استاتیک_setAgeMethod ذخیره شده و در فراخوانیهای بعدی دوباره استفاده میشود. این روش هزینه اولیه جستجو را تنها یکبار پرداخت میکند و عملکرد را بهبود میبخشد.Invoke در Reflection کند است، زیرا شامل تبدیل نوع (Boxing) آرگومانها و بررسیهای امنیتی در زمان اجرا میشود. یک راهکار بهتر، تبدیل MethodInfo به یک Delegate است. Delegateها کد را به صورت مستقیم اجرا میکنند و عملکردی نزدیک به فراخوانی ایستا دارند. مثال زیر این تکنیک را نشان میدهد:public class DelegateExample
{
private static readonly Action<Person, int> _setAgeDelegate =
(Action<Person, int>)Delegate.CreateDelegate(
typeof(Action<Person, int>),
typeof(Person).GetMethod("SetAge"));
public void SetAgeWithDelegate(Person person, int age)
{
_setAgeDelegate(person, age);
}
}_setAgeDelegate یکبار ایجاد شده و سپس به سرعت فراخوانی میشود. این روش به مراتب سریعتر از Invoke است و برای سناریوهایی که عملکرد حیاتی است، ایدهآل محسوب میشود.using System.Linq.Expressions;
public class ExpressionExample
{
private static readonly Func<Person, int> _getAgeCompiled;
static ExpressionExample()
{
var parameter = Expression.Parameter(typeof(Person), "p");
var property = Expression.Property(parameter, "Age");
var lambda = Expression.Lambda<Func<Person, int>>(property, parameter);
_getAgeCompiled = lambda.Compile();
}
public int GetAgeWithExpression(Person person)
{
return _getAgeCompiled(person);
}
}Age ساخته شده و سپس به یک Delegate کامپایل میشود. نتیجه، یک فراخوانی سریع و بهینه است که نیازی به جستجوی مکرر متادیتا ندارد. این روش بهویژه زمانی مفید است که بخواهید دسترسی به خصوصیات یا متدها را به صورت پویا و با عملکرد بالا مدیریت کنید.Reflection.Emit به شما اجازه میدهد کد IL (Intermediate Language) را در زمان اجرا تولید کنید، که عملکرد بهتری نسبت به Reflection معمولی دارد. مثال ساده زیر استفاده از Reflection.Emit را نشان میدهد:using System.Reflection.Emit;
public class EmitExample
{
private static readonly DynamicMethod _dynamicMethod;
static EmitExample()
{
_dynamicMethod = new DynamicMethod(
"SetAgeDynamic",
null,
new[] { typeof(Person), typeof(int) },
typeof(EmitExample).Module);
ILGenerator il = _dynamicMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // Load Person instance
il.Emit(OpCodes.Ldarg_1); // Load age value
il.Emit(OpCodes.Callvirt, typeof(Person).GetMethod("SetAge"));
il.Emit(OpCodes.Ret);
_setAgeDynamic = (Action<Person, int>)_dynamicMethod.CreateDelegate(typeof(Action<Person, int>));
}
private static readonly Action<Person, int> _setAgeDynamic;
public void SetAgeWithEmit(Person person, int age)
{
_setAgeDynamic(person, age);
}
}SetAge را فراخوانی میکند و نیازی به Reflection در زمان اجرا ندارد. همچنین، Source Generators در داتنت میتوانند کد مورد نیاز را در زمان کامپایل تولید کنند و وابستگی به Reflection را به طور کامل حذف کنند.Person را با استفاده از پارامترهای مشخص ایجاد کنیم:public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Person(int id, string firstName, string lastName)
{
Id = id;
FirstName = firstName;
LastName = lastName;
}
}
public class AdvanceActivator
{
private static readonly Dictionary<Type, Delegate> _constructors = new();
public static T CreateInstance<T>(params object[] args)
{
var type = typeof(T);
if (!_constructors.TryGetValue(type, out var constructor))
{
var ctor = type.GetConstructor(args.Select(a => a.GetType()).ToArray());
constructor = Delegate.CreateDelegate(typeof(Func<object[], T>), ctor);
_constructors[type] = constructor;
}
return ((Func<object[], T>)constructor)(args);
}
}
// استفاده
Person person = AdvanceActivator.CreateInstance<Person>(10, "User", "Name");
Console.WriteLine($"{person.Id}, {person.FirstName}, {person.LastName}");Reflection.Emit یا Source Generators، میتوان کارایی برنامه را به طور قابلتوجهی بهبود داد. مثالهای ارائهشده در این مقاله نشان میدهند که چگونه میتوان این تکنیکها را در عمل پیادهسازی کرد.