C#: نظرة عامة على الميزات المتقدمة
1. التأمل
تتيح ميزة «الانعكاس» (Reflection) لأي برنامج فحص معلومات النوع الخاصة به ومعالجتها أثناء وقت التشغيل. وتُعد فئة Type جوهر ميزة الانعكاس، حيث تتيح الوصول إلى البيانات الوصفية مثل الحقول والخصائص والطرق.
(1) الحصول على معلومات النوع
using System;
using System.Reflection;
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public void SayHello() => Console.WriteLine("Hello!");
}
class Program
{
static void Main()
{
Type type1 = typeof(Person);
Type type2 = new Person().GetType();
Console.WriteLine($"Type name: {type1.Name}");
Console.WriteLine("Properties:");
foreach (PropertyInfo p in type1.GetProperties())
Console.WriteLine($" {p.Name} ({p.PropertyType.Name})");
Console.WriteLine("Methods:");
foreach (MethodInfo m in type1.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
Console.WriteLine($" {m.Name}");
}
}
Type name: Person
Properties:
Name (String)
Age (Int32)
Methods:
get_Name
set_Name
get_Age
set_Age
SayHello
(2) إنشاء مثيلات ديناميكيًا
using System;
using System.Reflection;
class Dog
{
public string Breed { get; set; } = "Unknown";
public override string ToString() => $"Dog: {Breed}";
}
class Program
{
static void Main()
{
Type type = typeof(Dog);
object obj = Activator.CreateInstance(type)!;
PropertyInfo prop = type.GetProperty("Breed")!;
prop.SetValue(obj, "Husky");
Console.WriteLine(obj);
}
}
Dog: Husky
▶ مثال
using System;
using System.Linq;
using System.Reflection;
class Calculator
{
public int Add(int a, int b) => a + b;
public double Multiply(double x, double y) => x * y;
public void Reset() { }
}
class Program
{
static void Main()
{
Calculator calc = new Calculator();
Type type = calc.GetType();
Console.WriteLine($"Type: {type.Name}");
Console.WriteLine($"Namespace: {type.Namespace}");
Console.WriteLine($"Is class: {type.IsClass}");
Console.WriteLine("Public methods:");
MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (MethodInfo m in methods)
{
string parameters = string.Join(", ", m.GetParameters().Select(p => $"{p.ParameterType.Name} {p.Name}"));
Console.WriteLine($" {m.ReturnType.Name} {m.Name}({parameters})");
}
}
}
Type: Calculator
Namespace:
Is class: True
Public methods:
Int32 Add(Int32 a, Int32 b)
Double Multiply(Double x, Double y)
Void Reset()
2. السمات
السمات هي علامات إعلانية تُستخدم لإضافة بيانات وصفية إلى عناصر الكود. يمكنك استخدام السمات المدمجة أو تعريف سمات مخصصة.
(1) السمات المدمجة
using System;
class LegacyService
{
[Obsolete("Please use NewService instead", error: false)]
public void OldMethod() => Console.WriteLine("Old method");
public void NewMethod() => Console.WriteLine("New method");
}
class Program
{
static void Main()
{
var service = new LegacyService();
service.OldMethod();
service.NewMethod();
}
}
Old method
New method
error الخاصة بـ Obsolete إلى true، فإن استدعاء الطريقة القديمة سيؤدي إلى ظهور خطأ في التحويل البرمجي بدلاً من تحذير.
(2) السمات المخصصة
using System;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
class DescriptionAttribute : Attribute
{
public string Text { get; }
public DescriptionAttribute(string text) => Text = text;
}
[Description("User management service")]
class UserService
{
[Description("Get all users")]
public void GetAll() { }
}
class Program
{
static void Main()
{
var attr = Attribute.GetCustomAttribute(typeof(UserService), typeof(DescriptionAttribute)) as DescriptionAttribute;
Console.WriteLine(attr?.Text ?? "No description");
}
}
User management service
3. تحليل متعمق للقيود العامة
تستخدم القيود العامة الكلمة الرئيسية where لتقييد نطاق معلمات النوع، مما يضمن استيفاءها لشروط محددة.
(1) نظرة عامة على أنواع القيود
| القيد | الوصف |
|---|---|
where T : class |
يجب أن يكون T من النوع المرجعي |
where T : struct |
يجب أن يكون T نوعًا قيمياً (لا يمكن أن يكون قيمة فارغة) |
where T : new() |
يجب أن تحتوي T على مُنشئ عام بدون معلمات |
where T : ISpecific |
يجب أن تنفذ T الواجهة المحددة |
where T : BaseClass |
يجب أن ترث T من الفئة الأساسية المحددة |
where T : unmanaged |
يجب أن يكون T نوعًا غير مُدار |
where T : notnull |
يجب أن يكون T نوعًا غير قابل للفراغ |
▶ مثال
using System;
interface IRepository
{
string GetName();
}
class SqlRepository : IRepository
{
public string GetName() => "SQL Repository";
}
class Service```<T>``` where T : class, IRepository, new()
{
private T _repo = new T();
public void Print() => Console.WriteLine(_repo.GetName());
}
class Program
{
static void Main()
{
var service = new Service<SqlRepository>();
service.Print();
}
}
SQL Repository
new() في آخر قائمة القيود؛ ويجب أن يأتي class أو struct أولاً.
4. المكررات (yield)
تستخدم أدوات التكرار yield return لإنتاج عناصر التسلسل واحدة تلو الأخرى، مما يحقق التقييم المتأخر — حيث لا يتم حساب العناصر إلا عند تكرارها.
(1) العائد على السندات وفترة انقطاع العائد
using System;
using System.Collections.Generic;
class Program
{
static IEnumerable```<int>``` GetNumbers()
{
for (int i = 0; i < 5; i++)
{
if (i == 3) yield break;
yield return i;
}
}
static void Main()
{
foreach (int n in GetNumbers())
Console.WriteLine(n);
}
}
0
1
2
yield break يوقف التكرار فورًا. تُرجع طرق المُكرر IEnumerable<T> أو IEnumerator<T>.
▶ مثال
using System;
using System.Collections.Generic;
class Program
{
static IEnumerable```<int>``` Fibonacci(int count)
{
int a = 0;
int b = 1;
for (int i = 0; i < count; i++)
{
yield return a;
int temp = a + b;
a = b;
b = temp;
}
}
static void Main()
{
Console.WriteLine("Fibonacci sequence, first 10 terms:");
foreach (int n in Fibonacci(10))
{
Console.Write(n + " ");
}
Console.WriteLine();
}
}
Fibonacci sequence, first 10 terms:
0 1 1 2 3 5 8 13 21 34
5. Span<T> و Memory<T>
يوفر كل من Span<T> وMemory<T> وصولاً مقسَّمًا دون نسخ إلى مناطق الذاكرة المتجاورة، مما يجعلهما نوعين أساسيين في سيناريوهات الأداء العالي.
(1) الاستخدام الأساسي لـ Span<T>
using System;
class Program
{
static void Main()
{
int[] array = { 10, 20, 30, 40, 50 };
Span```<int>``` span = array.AsSpan();
Span```<int>``` slice = span.Slice(1, 3);
slice[0] = 99;
Console.WriteLine(string.Join(", ", array));
Span```<int>``` stackSpan = stackalloc int[3];
stackSpan[0] = 1;
stackSpan[1] = 2;
stackSpan[2] = 3;
Console.WriteLine(string.Join(", ", stackSpan.ToArray()));
}
}
10, 99, 30, 40, 50
1, 2, 3
Span<T> إلا في المكدس؛ ولا يمكن استخدامه كحقل فئة أو عبر حدود تعليق (await). استخدم Memory<T> عندما تحتاج إلى التوسع عبر إطارات المكدس.
6. التعبيرات النمطية (Regex)
تُستخدم فئة System.Text.RegularExpressions.Regex لمطابقة الأنماط والبحث عن النص واستبداله.
(1) العمليات الشائعة
using System;
using System.Text.RegularExpressions;
class Program
{
static void Main()
{
string input = "Contact phone: 138-1234-5678, Email: test@example.com";
bool hasPhone = Regex.IsMatch(input, @"\d{3}-\d{4}-\d{4}");
Console.WriteLine($"Contains phone: {hasPhone}");
Match phoneMatch = Regex.Match(input, @"\d{3}-\d{4}-\d{4}");
Console.WriteLine($"Phone: {phoneMatch.Value}");
MatchCollection emails = Regex.Matches(input, @"[\w.]+@[\w.]+");
foreach (Match m in emails)
Console.WriteLine($"Email: {m.Value}");
string replaced = Regex.Replace(input, @"\d{4}", "****");
Console.WriteLine($"Replaced: {replaced}");
}
}
Contains phone: True
Phone: 138-1234-5678
Email: test@example.com
Replaced: Contact phone: 138-****-****, Email: test@example.com
(2) مرجع سريع للأنماط الشائعة
| النمط | الوصف |
|---|---|
\d |
رقم |
\w |
حرف/رقم/شرطة سفلية |
\s |
مسافة بيضاء |
. |
أي حرف (باستثناء حرف نهاية السطر) |
^ / $ |
بداية السطر / نهاية السطر |
{n,m} |
كرر n إلى m مرات |
[abc] |
مجموعة الأحرف |
7. كود غير آمن
يسمح سياق unsafe باستخدام المؤشرات وعمليات الذاكرة اليدوية، وهو ما ينطبق على سيناريوهات التوافق أو سيناريوهات الأداء الفائق.
(1) المؤشرات والقيم الثابتة
using System;
class Program
{
static unsafe void Main()
{
int value = 42;
int* ptr = &value;
Console.WriteLine($"Value: {*ptr}");
Console.WriteLine($"Address: {(long)ptr}");
int[] arr = { 1, 2, 3 };
fixed (int* p = arr)
{
Console.WriteLine($"First element: {*p}");
Console.WriteLine($"Second element: {*(p + 1)}");
}
}
}
Value: 42
Address: 12345678
First element: 1
Second element: 2
unsafe تمكين <AllowUnsafeBlocks>true</AllowUnsafeBlocks>`` in the project file. The fixed`؛ حيث تعمل هذه العبارة على تثبيت الكائن لمنع آلية جمع القمامة (GC) من نقل الذاكرة.
8. نظرة عامة على ميزات إصدارات لغة C# (7–12)
| الإصدار | الميزات الرئيسية | وصف موجز |
|---|---|---|
| C# 7 | المتغيرات ذات النوع «out»، المجموعات، مطابقة الأنماط | out int x الإعلان المضمن، (int, string) المجموعات، is Type الأنماط |
| C# 7 | الدوال المحلية، قيم الإرجاع من نوع ref | الدوال المتداخلة داخل الطرق، قيم الإرجاع من نوع المرجع |
| C# 8 | الأنواع المرجعية القابلة للقيمة «null»، التدفقات غير المتزامنة | string? علامة «nullable»، IAsyncEnumerable<T> |
| C# 8 | طرق الواجهة الافتراضية، والفهارس، والنطاقات | تطبيقات الواجهة الافتراضية، [^1]، [1..3] |
| C# 9 | أنواع السجلات، مُعيِّنات التهيئة | record الأنواع غير القابلة للتغيير، init الخصائص المخصصة للتهيئة فقط |
| C# 9 | العبارات ذات المستوى الأعلى، وnew المحددة النوع |
عدم وجود النص النمطي لـMain، new() حذف النوع |
| C# 10 | استخدام "using" عالمي، مساحات الأسماء على نطاق الملف | global using X;، namespace Ns; سطر واحد |
| C# 10 | هياكل «record»، سلاسل نصية ثابتة مع إدراج قيم | record struct، سلاسل نصية ثابتة مع إدراج قيم |
| C# 11 | القيم الثابتة للسلاسل الخام، إلزامية | """...""" السلاسل الخام متعددة الأسطر، required التهيئة الإجبارية |
| C# 11 | أنماط القوائم، الثوابت UTF-8 | [1, 2, ..] المطابقة، "hello"u8 |
| C# 12 | المُنشِئات الأولية | class C(int x) إعلان الفئة كمُنشِئ |
| C# 12 | تعبيرات المجموعات، الاسم المستعار لأي نوع | [1, 2, 3] التهيئة الموحدة، using P = int* |
❓ أسئلة شائعة
<T> و Memory<T>؟📖 ملخص
- تتيح ميزة «Reflection» الوصول إلى بيانات تعريف الأنواع أثناء وقت التشغيل من خلال فئة «Type»، ويمكنها إنشاء مثيلات ديناميكيًا
- تضيف السمات بيانات وصفية إعلانية إلى الكود، مما يدعم التعريفات المخصصة وقراءتها أثناء وقت التشغيل
- تقيّد القيود العامة (where) معلمات الأنواع بأن تكون أنواعًا مرجعية، أو أنواعًا قيمية، أو أنواعًا ذات منشئات، وما إلى ذلك.
- تعبير
yield returnيُنفذ التكرار المؤجل؛ أما تعبيرyield breakفيُنهي عملية التعداد - توفر Span
<T>تقطيعًا بدون نسخ يعتمد على المكدس؛ ويمكن لـ Memory<T>عبور حدود العمليات غير المتزامنة - تدعم فئة Regex مطابقة الأنماط والبحث والاستبدال وعمليات نصية أخرى
- يسمح السياق غير الآمن بإجراء عمليات المؤشر، مما يستلزم استخدام «fixed» لتثبيت الذاكرة
- استمر إصدار C# 7–12 في تقديم المجموعات (tuples)، والمراجع القابلة للقيمة "null"، وأنواع السجلات (record types)، والمُنشِئات الأولية (primary constructors)، وغيرها
📝 تمارين
- استخدم ميزة «الانعكاس» للحصول على جميع أسماء الطرق العامة للفئة
stringوعرضها - إنشاء سمة مخصصة
[Author]، وتطبيقها على فئة، وقراءتها عبر الانعكاس - اكتب دالة عامة
T Create``<T>``() where T : new()تقوم بإنشاء مثيل وإرجاعه - استخدم دالة
yieldلتنفيذ مُكرر يُنتج أول 20 حدًّا من متتابعة فيبوناتشي - استخدم التعبير النمطي (Regex) للتحقق مما إذا كانت السلسلة تتوافق مع تنسيق عنوان IPv4 صحيحًا