کش کردن مقادیر Enum در C# 14 با استفاده از Extension Everything – راهحل بهینه و thread-safe
نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۱۱/۰۹ ۱۰:۳۵
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
enum در سیشارپ، گرفتن لیست تمام مقادیر آن با متد Enum.GetValues است. این متد اگرچه بسیار راحت است، اما در مسیرهای داغ (hot path) برنامه یا داخل حلقهها میتواند به دلیل استفاده از reflection داخلی، تخصیص حافظه تکراری ایجاد کند و عملکرد را تحت تأثیر قرار دهد.enum نوشت. سؤال مهمی که خیلی از توسعهدهندگان مطرح میکنند این است:آیا میتوان نتیجهٔ Enum.GetValues را برای هر نوع enum بهصورت جداگانه کش (cache) کرد؟و آیا میتوان این کار را با یک فیلد خصوصی ساده داخل بدنهٔ extension انجام داد؟static ساده داخل کلاس extension.public static class EnumExtensions
{
extension<T>(T _) where T : struct, Enum
{
private static T[] _cache;
public static T[] GetValues()
{
return _cache ??= Enum.GetValues<T>();
}
}
}static داخل کلاس معمولی (حتی اگر داخل extension نوشته شود)، برای تمام انواع T مشترک است. یعنی اگر اول MyEnum1 فراخوانی شود، _cache با مقادیر MyEnum1 پر میشود. سپس وقتی MyEnum2 را صدا بزنیم، همان آرایهٔ اشتباه قبلی را برمیگرداند!public static class EnumExtensions
{
extension<T>(T _) where T : struct, Enum
{
/// <summary>
/// برمیگرداند آرایهای از تمام مقادیر تعریفشده در این enum
/// (کششده، thread-safe و بدون تخصیص اضافی پس از بار اول)
/// </summary>
public static T[] GetValues() => Cache<T>.Values;
}
// کلاس کمکی – برای هر T یک نمونهٔ جداگانه از فیلدهای static دارد
private static class Cache<T> where T : struct, Enum
{
internal static readonly T[] Values = Enum.GetValues<T>();
}
}| ویژگی | توضیح |
| Lazy Initialization | CLR خودش تضمین میکند که فیلد static readonly فقط یکبار مقداردهی شود |
| Thread-Safe | بدون نیاز به lock یا Interlocked – کاملاً ایمن در محیط چندرشتهای |
| Zero allocation بعد از بار اول | پس از اولین دسترسی، هیچ تخصیص heap جدیدی رخ نمیدهد |
| بدون Dictionary | نیازی به نگهداری کش در Dictionary نیست؛ سریعتر و سبکتر |
| تمیز و خوانا | کد کاملاً واضح و قابل نگهداری است |
// گزینه ۱ – ساده و سبک
internal static readonly IReadOnlyList<T> Values
= Array.AsReadOnly(Enum.GetValues<T>());
// گزینه ۲ – کاملاً immutable (نیاز به System.Collections.Immutable)
internal static readonly ImmutableArray<T> Values
= Enum.GetValues<T>().ToImmutableArray();extension صرفاً یک شیرینسازی نحوی (syntactic sugar) است.enum بنویسیم، اما برای کش کردن مقادیر وابسته به نوع، همچنان باید به الگوی کلاسیک generic static holder تکیه کنیم. این الگو ترکیبی عالی از عملکرد، ایمنی رشتهای و خوانایی کد ارائه میدهد و در پروژههای بزرگ و حساس به عملکرد، بارها خودش را ثابت کرده است.// استفاده در کد شما اینقدر ساده و زیبا خواهد بود: MyStatus.GetValues(); // → T[] UserRole.GetValues(); // → آرایهٔ جدا برای این enum