الـ 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) لربط الخدمات تلقائيًا.
التحدي
سهللنقم ببناء نظام لاكتشاف المكونات الإضافية (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- غير مميزة بأي سمة (هذه مجرد فئة عادية)
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})
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةNamespaces والتوجيهاتمقدمة في Classes و Objectsالكلمة المفتاحية 'this'Methods والمعاملاتFields مقابل PropertiesConstructorsObject Initializersمراجعة - آلة حاسبة بسيطة4الوراثة
صيغة الوراثة الأساسية (:)الكلمة المفتاحية 'base'الكلمات المفتاحية Virtual و Overrideالأصناف المختومة (Sealed Classes)الفئة الأساسية 'object'مراجعة - هيكلية الموظفين7ميزات متقدمة
تحميل العوامل (Operator Overloading)المفهرسات (Indexers (this[]))إعادة تعريف ()ToStringدوال التوسعة (Extension Methods)ملخص - Custom List2الخصائص والأعضاء الساكنة (Static Members)
الخصائص المطبقة تلقائيًا (Auto-Implemented Properties)خصائص القراءة فقط والكتابة فقطالحقول والدوال الساكنة (Static)الفئات الساكنة (Static Classes)الأعضاء ذات البنية التعبيرية (Expression-Bodied Members)5تعدد الأشكال والواجهات (Polymorphism & Interfaces)
تعدد الأشكال: وقت التجميع مقابل وقت التشغيلالواجهة مقابل الفئة المجردة (Abstract Class)الواجهات المتعددةالواجهات الصريحةUpcasting و Downcastingملخص - حاسبة الأشكال8مفاهيم البرمجة كائنية التوجه (OOP) المتقدمة
التركيب بدلاً من الوراثةالـ Generics (الأصناف والدوال)الـ Delegates والأحداثالـ Attributes والـ Reflectionواجهة IDisposable وجملة usingأساسيات Dependency Injection