Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

الـ Attributes والـ Reflection

جزء من قسم Object Oriented Programming في رحلة C# على Coddy — الدرس 44 من 70.

السمات (Attributes) هي علامات بيانات وصفية ترفقها بعناصر الكود مثل الأصناف (classes) أو الأساليب (methods) أو الخصائص (properties). هي لا تغير كيفية تنفيذ الكود بشكل مباشر، ولكنها توفر معلومات يمكن قراءتها في وقت التشغيل (runtime). الانعكاس (Reflection) هو الآلية التي تقرأ هذه البيانات الوصفية.

تقوم بتطبيق سمة (attribute) عن طريق وضعها داخل أقواس مربعة فوق العنصر المستهدف:

[Obsolete("Use NewMethod instead")]
public void OldMethod() { }

[Serializable]
public class Person
{
    public string Name { get; set; }
}

يمكنك إنشاء سمات مخصصة من خلال الوراثة من Attribute:

public class AuthorAttribute : Attribute
{
    public string Name { get; }
    public AuthorAttribute(string name) => Name = name;
}

[Author("John")]
public class Calculator { }

يتيح لك الانعكاس (Reflection) فحص الأنواع وقراءة السمات في وقت التشغيل باستخدام مساحة الأسماء System.Reflection:

Type type = typeof(Calculator);
var attr = type.GetCustomAttribute<AuthorAttribute>();
Console.WriteLine(attr.Name);  // John

يمكنك أيضاً اكتشاف الأساليب (methods)، والخصائص (properties)، واستدعاؤها ديناميكياً:

MethodInfo[] methods = type.GetMethods();
foreach (var method in methods)
    Console.WriteLine(method.Name);

تُدير السمات (Attributes) والانعكاس (reflection) العديد من أطر العمل - حيث تستخدمها مكتبات التسلسل (serialization) للتحكم في مخرجات JSON، وتستخدمها أطر عمل الاختبار لاكتشاف دوال الاختبار (test methods)، وتستخدمها حاويات حقن التبعية (dependency injection) لربط الخدمات تلقائيًا.

challenge icon

التحدي

سهل

لنقم ببناء نظام لاكتشاف المكونات الإضافية (plugin discovery system) يستخدم السمات المخصصة (custom attributes) والانعكاس (reflection) للعثور تلقائياً على الفئات التي تم تمييزها كمكونات إضافية ووصفها. هذا مشابه لكيفية اكتشاف أطر العمل الحقيقية للمكونات، أو طرق الاختبار، أو نقاط نهاية واجهة برمجة التطبيقات (API endpoints) في وقت التشغيل.

ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:

  • PluginAttribute.cs: قم بإنشاء سمة مخصصة تسمى PluginAttribute في مساحة الأسماء PluginSystem. يجب أن تقوم السمة الخاصة بك بـ:
    • الوراثة من Attribute
    • امتلاك خاصيتين للقراءة فقط: Name (string) و Version (string)
    • قبول كلتا القيمتين من خلال منشئ الفئة (constructor) الخاص بها
  • Plugins.cs: قم بإنشاء عدة فئات للمكونات الإضافية في مساحة الأسماء PluginSystem. يجب تزيين بعضها بـ PluginAttribute الخاص بك، ويجب ألا يتم تزيين أحدها:
    • ImageProcessor - مميزة بـ [Plugin("Image Processor", "1.0")]
    • DataExporter - مميزة بـ [Plugin("Data Exporter", "2.5")]
    • HelperUtility - غير مميزة بأي سمة (هذه مجرد فئة عادية)
    يمكن أن تكون كل فئة فارغة - نحن مهتمون فقط باكتشاف بياناتها الوصفية (metadata) من خلال الانعكاس.
  • Program.cs: في ملفك الرئيسي، استخدم الانعكاس لاكتشاف جميع الفئات التي تحتوي على PluginAttribute. لكل مكون إضافي تم اكتشافه، قم بطباعة معلوماته. ستحتاج إلى:
    • الحصول على جميع الأنواع (types) من التجميع الحالي (assembly)
    • فحص كل نوع بحثاً عن PluginAttribute
    • استخراج وعرض خصائص Name و Version الخاصة بالسمة

استخدم System.Reflection لفحص الأنواع واسترداد السمات المخصصة. ترجع الطريقة GetCustomAttribute<T>() القيمة null إذا لم تكن السمة موجودة، لذا يمكنك استخدام هذا لتصفية الفئات التي تعد مكونات إضافية فعلية.

اطبع كل مكون إضافي تم اكتشافه بهذا التنسيق، واحد في كل سطر:

Plugin: {Name} (v{Version})

يجب طباعة المكونات الإضافية بالترتيب: ImageProcessor أولاً، ثم DataExporter. يجب ألا تظهر فئة HelperUtility في المخرجات لأنها تفتقر إلى السمة.

المخرجات المتوقعة:

Plugin: Image Processor (v1.0)
Plugin: Data Exporter (v2.5)

يوضح هذا التحدي كيف تستخدم أطر العمل السمات كعلامات والانعكاس لاكتشافها - وهي نفس التقنية التي تدعم حاويات حقن التبعية (dependency injection containers)، ومكتبات التسلسل (serialization libraries)، وأطر عمل الاختبار!

ورقة مرجعية

السمات (Attributes) هي علامات بيانات وصفية توضع داخل أقواس مربعة فوق عناصر الكود. وهي توفر معلومات يمكن قراءتها في وقت التشغيل (runtime):

[Obsolete("Use NewMethod instead")]
public void OldMethod() { }

[Serializable]
public class Person
{
    public string Name { get; set; }
}

قم بإنشاء سمات مخصصة عن طريق الوراثة من Attribute:

public class AuthorAttribute : Attribute
{
    public string Name { get; }
    public AuthorAttribute(string name) => Name = name;
}

[Author("John")]
public class Calculator { }

يتيح لك الانعكاس (Reflection) فحص الأنواع وقراءة السمات في وقت التشغيل باستخدام مساحة الأسماء System.Reflection:

Type type = typeof(Calculator);
var attr = type.GetCustomAttribute<AuthorAttribute>();
Console.WriteLine(attr.Name);  // John

اكتشف الأساليب (methods) والخصائص (properties) ديناميكيًا:

MethodInfo[] methods = type.GetMethods();
foreach (var method in methods)
    Console.WriteLine(method.Name);

تُرجع الطريقة GetCustomAttribute<T>() القيمة null إذا لم تكن السمة موجودة، مما يسمح لك بتصفية الأنواع بناءً على وجود السمة.

جرّب بنفسك

using System;
using System.Reflection;
using PluginSystem;

class Program
{
    public static void Main(string[] args)
    {
        // TODO: الحصول على جميع الأنواع من التجميع الحالي
        // تلميح: استخدم Assembly.GetExecutingAssembly().GetTypes()
        
        // TODO: قم بالمرور عبر كل نوع وتحقق مما إذا كان يحتوي على PluginAttribute
        // تلميح: استخدم type.GetCustomAttribute<PluginAttribute>()
        
        // TODO: إذا كانت السمة موجودة، قم بطباعة معلومات المكون الإضافي بالتنسيق التالي:
        // Plugin: {Name} (v{Version})
        
    }
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming