معرفی راه جدیدی برای Reflection در داتنت ۸
نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۱/۱۶ ۲۰:۰۴
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
UnsafeAccessorAttribute معرفی شدهاست که راهی سریعتر و کارآمدتر برای دسترسی به اعضای خصوصی یا داخلی (Internal) کلاسها ارائه میدهد. GetField یا GetMethod از کلاس Type بود که انعطافپذیری بالایی داشت، اما دو مشکل اصلی داشت: اول، عملکرد کند آن به دلیل بار اضافی در زمان اجرا (Runtime)؛ دوم، ناسازگاری با AOT. در AOT، فرآیند بهینهسازی مانند هرس درخت (Tree-Shaking) کدهای استفادهنشده را حذف میکند، اما بازتاب به دلیل پویاییاش، نمیتواند بهخوبی با این فرآیند هماهنگ شود و ممکن است به خطاهای غیرمنتظره منجر شود. ویژگی UnsafeAccessorAttribute برای حل این مشکلات طراحی شده است. این ابزار با ارائه دسترسی مستقیمتر به اعضای خصوصی، هم عملکرد را بهبود میبخشد و هم سازگاری با AOT را تضمین میکند.UnsafeAccessorAttribute یک ویژگی (Attribute) در فضای نام System.Runtime.CompilerServices است که به توسعهدهندگان اجازه میدهد به اعضای غیرقابلدسترس (Inaccessible Members) مانند فیلدها (Fields)، متدها (Methods) یا سازندهها (Constructors) دسترسی پیدا کنند. این ویژگی روی متدهای ایستا و خارجی (Static Extern Methods) اعمال میشود و پیادهسازی آن توسط Runtime داتنت بهصورت پویا تأمین میشود. این ابزار از سه نوع دسترسی پشتیبانی میکند:UnsafeAccessorAttribute مشخص کنید. اگر عضو موردنظر پیدا نشود، Runtime داتنت، خطای MissingFieldException یا MissingMethodException را تولید میکند.Counter داریم که یک فیلد خصوصی به نام _count دارد:public class Counter
{
private int _count = 0;
public int CountPlusOne => _count + 1;
}_count با استفاده از UnsafeAccessorAttribute, میتوانیم کد زیر را بنویسیم:public class Accessor
{
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_count")]
public static extern ref int GetCountField(Counter counter);
}
// استفاده
var counter = new Counter();
Accessor.GetCountField(counter) = 100;
Console.WriteLine(counter.CountPlusOne); // خروجی: 101ref امکان تغییر مستقیم مقدار فیلد را فراهم میکند. این روش بسیار سریعتر از بازتاب است، زیرا نیازی به جستوجوی پویا در متادیتا ندارد.UnsafeAccessorAttribute دسترسی تقریباً مستقیم به عضو را فراهم میکند. بنچمارکها نشان میدهند که سرعت آن به متدهای عادی بسیار نزدیک است.System.Text.Json را فراهم میکند.[MemoryDiagnoser]
public class Benchmark
{
private readonly Counter _counter = new();
[Benchmark]
public int DotNet7()
{
typeof(Counter)
.GetField("_count", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(_counter, 100);
return _counter.CountPlusOne;
}
[Benchmark]
public int DotNet8()
{
GetCountField(_counter) = 100;
return _counter.CountPlusOne;
}
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_count")]
private static extern ref int GetCountField(Counter counter);
}UnsafeAccessorAttribute) بهطور قابلتوجهی سریعتر از روش داتنت ۷ (با بازتاب) است.System.Text.Json که نیاز به دسترسی به فیلدهای خصوصی برای سریالسازی دارد.public class UnsafeAccessorExample
{
private string UnsafeMethod()
{
return "Unsafe Name";
}
}
public class UnsafeAccessorClass
{
[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "UnsafeMethod")]
public static extern string GetUnsafeMethod(UnsafeAccessorExample unsafeExample);
}
// استفاده
var example = new UnsafeAccessorExample();
var result = UnsafeAccessorClass.GetUnsafeMethod(example);
Console.WriteLine(result); // خروجی: Unsafe NameUnsafeAccessorAttribute بدون خطر نیست:UnsafeAccessorAttribute در داتنت ۸ یک ابزار قدرتمند و نوآورانه است که جایگزینی سریع و کارآمد را برای بازتاب ارائه میدهد. این ویژگی با بهبود عملکرد و پشتیبانی از AOT، به توسعهدهندگان اجازه میدهد تا بهصورت امنتر و مؤثرتری به اعضای خصوصی دسترسی پیدا کنند. با این حال، استفاده از آن نیازمند دقت و مسئولیت است، زیرا میتواند به نقض اصول کپسولهسازی و مشکلات نگهداری کد منجر شود. برای توسعهدهندگانی که در پروژههای حساس به عملکرد یا سازگار با AOT کار میکنند، این ابزار یک گزینه ارزشمند است. پیشنهاد میشود پیش از استفاده گسترده، با تستهای کامل و مستندسازی دقیق، از پایداری کد اطمینان حاصل کنید.