C#: الأنواع القابلة للإلغاء ومطابقة الأنماط

1. أنواع القيم القابلة للصفر

لا يمكن أن تكون أنواع القيم null بشكل افتراضي. تسمح البنية Nullable<T> (الاختصار T?) لأنواع القيم بأن تحمل دلالات القيمة «null».

CSHARP
int? a = 42;
int? b = null;
bool? flag = null;
double? price = 9.99;

Console.WriteLine(a.HasValue);
Console.WriteLine(b.HasValue);
Console.WriteLine(flag.GetValueOrDefault());
Console.WriteLine(price.GetValueOrDefault(0.0));
TEXT 📖 للعرض فقط
True
False
False
9.99

▶ مثال

CSHARP
int? score = null;

Console.WriteLine(score.HasValue);
Console.WriteLine(score.GetValueOrDefault());
Console.WriteLine(score.GetValueOrDefault(60));

score = 88;
Console.WriteLine(score.Value);
▶ جرّب الكود
TEXT 📖 للعرض فقط
False
0
60
88

2. دمج القيم الفارغة والمُشغِّلات الشرطية للقيم الفارغة

يوفر ?? قيمة افتراضية، ويقوم ?. بالوصول إلى العناصر بأمان، بينما يُعلم ! المُترجم بأن «هذه القيمة ليست فارغة هنا».

CSHARP
int? x = null;
int y = x ?? 0;

string? name = null;
int? len = name?.Length;

string s = null!;
Console.WriteLine(s is null);
TEXT 📖 للعرض فقط
True

▶ مثال

CSHARP
string? input = null;

string display = input ?? "(empty)";
Console.WriteLine(display);

int? length = input?.Length;
Console.WriteLine(length ?? -1);

string? city = "Beijing";
Console.WriteLine(city?.Length ?? 0);
▶ جرّب الكود
TEXT 📖 للعرض فقط
(empty)
-1
7

3. أنواع المراجع القابلة للقيمة الفارغة

ابتداءً من الإصدار C# 8، يمكنك استخدام #nullable enable لتفعيل تحذيرات أنواع المراجع القابلة للقيمة «null». تسمح string? بقيمة «null»، بينما لا تسمح string بذلك.

CSHARP
#nullable enable
string? maybeNull = null;
string notNull = "hello";

maybeNull = null;
notNull = null!;

▶ مثال

CSHARP
#nullable enable
string Greet(string? name)
{
    return name is null ? "Hello, stranger" : $"Hello, {name}";
}

Console.WriteLine(Greet(null));
Console.WriteLine(Greet("Alice"));
▶ جرّب الكود
TEXT 📖 للعرض فقط
Hello, stranger
Hello, Alice

4. استراتيجيات التحقق من القيم الفارغة

الإستراتيجية الاستخدام السيناريو
فحص صريح if (x is null) يلزم وجود منطق فرعي
دمج القيم الفارغة x ?? defaultValue توفير قيمة افتراضية
شرط فارغ x?.Member الوصول الآمن إلى العناصر

▶ مثال

CSHARP
string? GetName() => null;

string? name = GetName();

if (name is null)
{
    Console.WriteLine("Name is null");
}

string upper = name?.ToUpper() ?? "DEFAULT";
Console.WriteLine(upper);
▶ جرّب الكود
TEXT 📖 للعرض فقط
Name is null
DEFAULT

5. مقدمة في مطابقة الأنماط

تجمع مطابقة الأنماط بين التحقق من النوع واستخراج المتغيرات في عملية واحدة، وتُستخدم عادةً مع is وswitch.

(1) هو النمط

CSHARP
object obj = 42;

if (obj is int i)
{
    Console.WriteLine($"Is integer: {i}");
}
TEXT 📖 للعرض فقط
Is integer: 42

▶ مثال

CSHARP
object value = "hello";

if (value is int n)
{
    Console.WriteLine(n);
}
else if (value is string s)
{
    Console.WriteLine($"String length: {s.Length}");
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
String length: 5

6. نمط الكتابة و"switch"

قم بتقسيم switch حسب النوع واستخرج المتغيرات.

▶ مثال

CSHARP
string Describe(object obj)
{
    return obj switch
    {
        int i => $"Integer {i}",
        double d => $"Double {d}",
        string s => $"String \"{s}\"",
        bool b => $"Boolean {b}",
        null => "null",
        _ => "Unknown type"
    };
}

Console.WriteLine(Describe(10));
Console.WriteLine(Describe(3.14));
Console.WriteLine(Describe("hi"));
Console.WriteLine(Describe(true));
▶ جرّب الكود
TEXT 📖 للعرض فقط
Integer 10
Double 3.14
String "hi"
Boolean True

7. نمط الخاصية

تتم مطابقة أنماط الخصائص بناءً على خصائص الكائنات دون الحاجة إلى استخراج القيم يدويًّا لإجراء المقارنة.

▶ مثال

CSHARP
var person = new { Name = "Alice", Age = 20 };

if (person is { Age: > 18 })
{
    Console.WriteLine("Adult");
}

string Category(object p) => p switch
{
    { Age: < 12 } => "Child",
    { Age: >= 12 and < 18 } => "Teenager",
    { Age: >= 18 } => "Adult",
    _ => "Unknown"
};

Console.WriteLine(Category(person));
▶ جرّب الكود
TEXT 📖 للعرض فقط
Adult
Adult

8. نوع السجل

قدمت لغة C# 9 ميزة record، التي توفر مقارنة المساواة على أساس القيمة والثبات بشكل افتراضي.

CSHARP
record Person(string Name, int Age);

▶ مثال

CSHARP
record Person(string Name, int Age);

var p1 = new Person("Alice", 18);
var p2 = new Person("Alice", 18);

Console.WriteLine(p1 == p2);
Console.WriteLine(ReferenceEquals(p1, p2));
Console.WriteLine(p1);
▶ جرّب الكود
TEXT 📖 للعرض فقط
True
False
Person { Name = Alice, Age = 18 }

9. باستخدام التعبير

يقوم with بإنشاء نسخة من سجل موجود مع تعديل بعض خصائصه، دون تغيير النسخة الأصلية.

▶ مثال

CSHARP
public class Student : IEquatable<Student>
{
    public string Name { get; }
    public int Age { get; }
    public string Grade { get; }
    public Student(string name, int age, string grade)
    {
        Name = name;
        Age = age;
        Grade = grade;
    }
    public Student With(int? age = null, string grade = null)
    {
        return new Student(Name, age ?? this.Age, grade ?? this.Grade);
    }
    public bool Equals(Student other)
    {
        return other != null && Name == other.Name && Age == other.Age && Grade == other.Grade;
    }
    public override bool Equals(object obj) => Equals(obj as Student);
    public override int GetHashCode() => System.HashCode.Combine(Name, Age, Grade);
    public override string ToString() => $"Student {{ Name = {Name}, Age = {Age}, Grade = {Grade} }}";
}

class Program
{
    static void Main()
    {
        var s1 = new Student("Bob", 20, "A");
        var s2 = s1.With(age: 21, grade: "A+");
        System.Console.WriteLine(s1);
        System.Console.WriteLine(s2);
    }
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
Student { Name = Bob, Age = 20, Grade = A }
Student { Name = Bob, Age = 21, Grade = A+ }

10. التوبول

تجمع المجموعات (Tuples) قيمًا متعددة في بنية خفيفة الوزن، وتدعم العناصر المسماة وعملية التفكيك.

▶ مثال

CSHARP
(int Id, string Name) person = (1, "Charlie");
Console.WriteLine(person.Id);
Console.WriteLine(person.Name);

var coords = (X: 3.0, Y: 4.0);
double distance = Math.Sqrt(coords.X * coords.X + coords.Y * coords.Y);
Console.WriteLine(distance);

(int x, int y) = (10, 20);
Console.WriteLine($"x={x}, y={y}");
▶ جرّب الكود
TEXT 📖 للعرض فقط
1
Charlie
5
x=10, y=20

11. تفكيك المجموعات ومطابقة الأنماط

يتيح الجمع بين المجموعات ومطابقة الأنماط معالجة الفروع ذات الشروط المتعددة بطريقة أنيقة.

▶ مثال

CSHARP
string Classify(int score, bool hasBonus) => (score, hasBonus) switch
{
    (>= 90, true) => "Excellent+Bonus",
    (>= 90, false) => "Excellent",
    (>= 60, true) => "Pass+Bonus",
    (>= 60, false) => "Pass",
    _ => "Fail"
};

Console.WriteLine(Classify(95, true));
Console.WriteLine(Classify(75, false));
Console.WriteLine(Classify(50, true));
▶ جرّب الكود
TEXT 📖 للعرض فقط
Excellent+Bonus
Pass
Fail

❓ أسئلة شائعة

س هل يمكن استخدام int? وint مباشرة في العمليات الحسابية؟
ج لا. يجب تحويلهما إلى قيمة غير فارغة باستخدام .Value أو ?? قبل إجراء أي عملية حسابية.
س ما الفرق بين string? وstring؟
ج يسمح string? بالتعيين بقيمة null، بينما سيصدر string تحذيرًا من المُجمِّع عند التعيين بقيمة null مع تمكين خاصية nullable.
س ما الفرق الجوهري بين «الرقم القياسي» و«الفئة»؟
ج يستخدم «الرقم القياسي» المساواة القائمة على القيمة (تقوم علامة == بمقارنة المحتوى)، بينما تستخدم «الفئة» المساواة القائمة على المرجع (تقوم علامة == بمقارنة عنوان الذاكرة).
س هل يمكن استخدام تعبير with مع class؟
ج لا. لا يعمل تعبير with إلا مع أنواع السجلات أو البنيات التي تحتوي على خصائص قابلة للتعيين فقط.
س هل هناك حد أقصى لعدد عناصر التوبول؟
ج تدعم ValueTuple ما يصل إلى 8 معلمات نوع؛ وما فوق ذلك، يتم استخدام TRest المتداخلة للتوسيع.

📖 ملخص

📝 تمارين

  1. قم بتعريف double? price = null;، واستخدم GetValueOrDefault(99.9) للحصول على القيمة الافتراضية وعرضها
  2. اكتب دالة تقبل معلمة string?، وتستخدم ?. و?? لإرجاع طول السلسلة أو -1
  3. استخدم النمط is للتحقق مما إذا كان object obj = 3.14; يساوي double d، ثم أخرج d * 2
  4. تعريف record Book(string Title, double Price);، وإنشاء مثيل، واستخدام with لتعديل السعر
  5. استخدم تعبير التبديل باستخدام التوبول للتحقق مما إذا كان (int month, int day) يمثل «يوم رأس السنة» أم «عيد الاستقلال»
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%