C#: الأنواع القابلة للإلغاء ومطابقة الأنماط
1. أنواع القيم القابلة للصفر
لا يمكن أن تكون أنواع القيم null بشكل افتراضي. تسمح البنية Nullable<T> (الاختصار T?) لأنواع القيم بأن تحمل دلالات القيمة «null».
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));
True
False
False
9.99
▶ مثال
int? score = null;
Console.WriteLine(score.HasValue);
Console.WriteLine(score.GetValueOrDefault());
Console.WriteLine(score.GetValueOrDefault(60));
score = 88;
Console.WriteLine(score.Value);
False
0
60
88
2. دمج القيم الفارغة والمُشغِّلات الشرطية للقيم الفارغة
يوفر ?? قيمة افتراضية، ويقوم ?. بالوصول إلى العناصر بأمان، بينما يُعلم ! المُترجم بأن «هذه القيمة ليست فارغة هنا».
int? x = null;
int y = x ?? 0;
string? name = null;
int? len = name?.Length;
string s = null!;
Console.WriteLine(s is null);
True
▶ مثال
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);
(empty)
-1
7
3. أنواع المراجع القابلة للقيمة الفارغة
ابتداءً من الإصدار C# 8، يمكنك استخدام #nullable enable لتفعيل تحذيرات أنواع المراجع القابلة للقيمة «null». تسمح string? بقيمة «null»، بينما لا تسمح string بذلك.
#nullable enable
string? maybeNull = null;
string notNull = "hello";
maybeNull = null;
notNull = null!;
▶ مثال
#nullable enable
string Greet(string? name)
{
return name is null ? "Hello, stranger" : $"Hello, {name}";
}
Console.WriteLine(Greet(null));
Console.WriteLine(Greet("Alice"));
Hello, stranger
Hello, Alice
4. استراتيجيات التحقق من القيم الفارغة
| الإستراتيجية | الاستخدام | السيناريو |
|---|---|---|
| فحص صريح | if (x is null) |
يلزم وجود منطق فرعي |
| دمج القيم الفارغة | x ?? defaultValue |
توفير قيمة افتراضية |
| شرط فارغ | x?.Member |
الوصول الآمن إلى العناصر |
▶ مثال
string? GetName() => null;
string? name = GetName();
if (name is null)
{
Console.WriteLine("Name is null");
}
string upper = name?.ToUpper() ?? "DEFAULT";
Console.WriteLine(upper);
Name is null
DEFAULT
5. مقدمة في مطابقة الأنماط
تجمع مطابقة الأنماط بين التحقق من النوع واستخراج المتغيرات في عملية واحدة، وتُستخدم عادةً مع is وswitch.
(1) هو النمط
object obj = 42;
if (obj is int i)
{
Console.WriteLine($"Is integer: {i}");
}
Is integer: 42
▶ مثال
object value = "hello";
if (value is int n)
{
Console.WriteLine(n);
}
else if (value is string s)
{
Console.WriteLine($"String length: {s.Length}");
}
String length: 5
6. نمط الكتابة و"switch"
قم بتقسيم switch حسب النوع واستخرج المتغيرات.
▶ مثال
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));
Integer 10
Double 3.14
String "hi"
Boolean True
7. نمط الخاصية
تتم مطابقة أنماط الخصائص بناءً على خصائص الكائنات دون الحاجة إلى استخراج القيم يدويًّا لإجراء المقارنة.
▶ مثال
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));
Adult
Adult
8. نوع السجل
قدمت لغة C# 9 ميزة record، التي توفر مقارنة المساواة على أساس القيمة والثبات بشكل افتراضي.
record Person(string Name, int Age);
▶ مثال
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);
True
False
Person { Name = Alice, Age = 18 }
9. باستخدام التعبير
يقوم with بإنشاء نسخة من سجل موجود مع تعديل بعض خصائصه، دون تغيير النسخة الأصلية.
▶ مثال
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);
}
}
Student { Name = Bob, Age = 20, Grade = A }
Student { Name = Bob, Age = 21, Grade = A+ }
10. التوبول
تجمع المجموعات (Tuples) قيمًا متعددة في بنية خفيفة الوزن، وتدعم العناصر المسماة وعملية التفكيك.
▶ مثال
(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}");
1
Charlie
5
x=10, y=20
11. تفكيك المجموعات ومطابقة الأنماط
يتيح الجمع بين المجموعات ومطابقة الأنماط معالجة الفروع ذات الشروط المتعددة بطريقة أنيقة.
▶ مثال
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));
Excellent+Bonus
Pass
Fail
❓ أسئلة شائعة
int? وint مباشرة في العمليات الحسابية؟.Value أو ?? قبل إجراء أي عملية حسابية.string? وstring؟string? بالتعيين بقيمة null، بينما سيصدر string تحذيرًا من المُجمِّع عند التعيين بقيمة null مع تمكين خاصية nullable.with مع class؟with إلا مع أنواع السجلات أو البنيات التي تحتوي على خصائص قابلة للتعيين فقط.📖 ملخص
T?هوNullable<T>، مما يسمح بأن تكون أنواع القيم ذات قيمة «null»HasValue/Value/GetValueOrDefault()الوصول إلى القيم القابلة للإلغاء??،?.،!تتعامل مع حالات القيمة الفارغة#nullable enableيُفعّل التحقق من أنواع المراجع القابلة للإلغاء- يُسهّل نمط
isونمط النوع ونمط الخاصية عملية التحقق من النوع - تعبر تعبيرات
switchالتي تستخدم مطابقة الأنماط عن المنطق متعدد الفروع بشكل موجز recordيوفر المساواة في القيمة والثباتwithيقوم بإنشاء نسخ من السجلات مع تعديل بعض خصائصها- تجمع المجموعات (Tuples) بين قيم متعددة بطريقة خفيفة الوزن، وتدعم التسمية والتفكيك
📝 تمارين
- قم بتعريف
double? price = null;، واستخدمGetValueOrDefault(99.9)للحصول على القيمة الافتراضية وعرضها - اكتب دالة تقبل معلمة
string?، وتستخدم?.و??لإرجاع طول السلسلة أو-1 - استخدم النمط
isللتحقق مما إذا كانobject obj = 3.14;يساويdouble d، ثم أخرجd * 2 - تعريف
record Book(string Title, double Price);، وإنشاء مثيل، واستخدامwithلتعديل السعر - استخدم تعبير التبديل باستخدام التوبول للتحقق مما إذا كان
(int month, int day)يمثل «يوم رأس السنة» أم «عيد الاستقلال»