عاملا الـ ? و !
جزء من قسم Object Oriented Programming في رحلة Dart على Coddy — الدرس 25 من 110.
عند العمل مع الأنواع التي تقبل القيمة الفارغة (nullable types)، توفر Dart عاملين أساسيين للوصول إلى القيم بأمان. عامل الوصول المدرك للقيم الفارغة (null-aware access operator) ?. و عامل تأكيد عدم الفراغ (null assertion operator) ! يمنحانك التحكم في كيفية التعامل مع القيم التي قد تكون فارغة.
يقوم العامل ?. بالوصول بأمان إلى الخصائص أو الدوال (methods) في قيمة قابلة لأن تكون فارغة (nullable). إذا كانت القيمة null، فإن التعبير بالكامل يعيد null بدلاً من الانهيار (crashing):
String? name = 'Alice';
print(name?.length); // 5
name = null;
print(name?.length); // null (لا يوجد تعطل!)يخبر عامل التشغيل ! لغة Dart "أنا متأكد من أن هذه القيمة ليست null." حيث يقوم بتحويل نوع قابل للقيمة الخالية (nullable) إلى نوع غير قابل للقيمة الخالية (non-nullable)، ولكنه يطرح خطأ إذا كانت القيمة في الواقع null:
String? maybeName = 'Bob';
String definite = maybeName!; // حسناً - نحن نؤكد أنها ليست null
print(definite.length); // 3
String? empty = null;
// String crash = empty!; // خطأ وقت التشغيل! القيمة كانت nullاستخدم ?. عندما تريد التعامل مع القيم الفارغة (null) بسلاسة. استخدم ! فقط عندما تكون متأكداً تماماً من وجود قيمة - ربما بعد التحقق منها باستخدام جملة if أو عندما تعلم أن المنطق يضمن وجود قيمة. إن الإفراط في استخدام ! يلغي الغرض من أمان القيم الفارغة (null safety) ويمكن أن يؤدي إلى توقف البرنامج المفاجئ (runtime crashes).
التحدي
سهللنقم ببناء نظام مخزون منتجات يوضح كيفية العمل بأمان مع القيم القابلة للإلغاء (nullable) باستخدام عاملي التشغيل ?. و !.
ستقوم بإنشاء ملفين لتنظيم الكود الخاص بك:
product.dart: قم بتعريف فئةProductالتي تمثل عنصراً في المخزون. بعض المنتجات لديها معلومات كاملة، بينما لا يزال البعض الآخر قيد الفهرسة. يجب أن تحتوي الفئة الخاصة بك على:- متغير
String nameغير قابل للإلغاء - يجب أن يكون لكل منتج اسم - متغير
double priceغير قابل للإلغاء - يجب أن يكون لكل منتج سعر - متغير
String? descriptionقابل للإلغاء - بعض المنتجات ليس لها وصف بعد - متغير
String? categoryقابل للإلغاء - بعض المنتجات لم يتم تصنيفها بعد - منشئ (constructor) يأخذ
nameوpriceكمعلمات مطلوبة - دالة
getDescriptionLength()تعيد طول الوصف باستخدام?.، أوnullإذا لم يكن هناك وصف - دالة
getCategoryUppercase()تعيد التصنيف بأحرف كبيرة باستخدام!- تفترض هذه الدالة أنه قد تم تعيين التصنيف - دالة
displayProduct()تطبع معلومات المنتج، وتعرض"No description"عندما يكون الوصف null و"Uncategorized"عندما يكون التصنيف null
- متغير
main.dart: استورد فئة المنتج الخاصة بك واستعرض كلا عاملي التشغيل:- أنشئ منتجاً باسم
'Laptop'بسعر999.99، ثم اضبط وصفه على'High-performance computing device'وتصنيفه على'Electronics' - أنشئ منتجاً باسم
'Mystery Item'بسعر49.99(اترك الوصف والتصنيف كـ null) - بالنسبة لـ Laptop: استدعِ
displayProduct()، واطبع'Description length: [length]'باستخدامgetDescriptionLength()، واطبع'Category: [uppercase]'باستخدامgetCategoryUppercase() - بالنسبة لـ Mystery Item: استدعِ
displayProduct()واطبع'Description length: [length]'باستخدامgetDescriptionLength()(سيظهر هذاnull)
- أنشئ منتجاً باسم
يجب أن تطبع الدالة displayProduct() بهذا التنسيق:
Product: [name] - $[price]
Description: [description or "No description"]
Category: [category or "Uncategorized"]المخرجات المتوقعة:
Product: Laptop - $999.99
Description: High-performance computing device
Category: Electronics
Description length: 32
Category: ELECTRONICS
Product: Mystery Item - $49.99
Description: No description
Category: Uncategorized
Description length: nullورقة مرجعية
يقوم معامل الوصول الآمن للقيم الفارغة (null-aware access operator) ?. بالوصول بأمان إلى الخصائص أو الطرق (methods) في القيم التي تقبل أن تكون فارغة (nullable). إذا كانت القيمة فارغة (null)، فإنه يعيد null بدلاً من إلقاء خطأ:
String? name = 'Alice';
print(name?.length); // 5
name = null;
print(name?.length); // null (لا يوجد انهيار!)يقوم معامل تأكيد عدم الفراغ (null assertion operator) ! بتحويل النوع الذي يقبل الفراغ (nullable) إلى نوع لا يقبل الفراغ (non-nullable). استخدمه فقط عندما تكون متأكداً من أن القيمة ليست فارغة، حيث سيؤدي إلى خطأ وقت التشغيل (runtime error) إذا كانت القيمة فارغة بالفعل:
String? maybeName = 'Bob';
String definite = maybeName!; // جيد - يؤكد أنها ليست فارغة
print(definite.length); // 3
String? empty = null;
// String crash = empty!; // خطأ وقت التشغيل!استخدم ?. للتعامل السلس مع القيم الفارغة، واستخدم ! فقط عندما تكون متأكداً تماماً من وجود قيمة.
جرّب بنفسك
import 'product.dart';
void main() {
// TODO: قم بإنشاء منتج باسم 'Laptop' وبسعر 999.99
// ثم قم بتعيين وصفه إلى 'High-performance computing device'
// وقم بتعيين فئته إلى 'Electronics'
// TODO: قم بإنشاء منتج باسم 'Mystery Item' وبسعر 49.99
// اترك الوصف والفئة كـ null
// TODO: بالنسبة لـ Laptop:
// - استدعِ displayProduct()
// - اطبع "Description length: [length]" باستخدام getDescriptionLength()
// - اطبع "Category: [uppercase]" باستخدام getCategoryUppercase()
// TODO: بالنسبة لـ Mystery Item:
// - استدعِ displayProduct()
// - اطبع "Description length: [length]" باستخدام getDescriptionLength()
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP
الملفات الخارجيةالمكتبات والـ Importsمقدمة في الـ OOPالـ Classes مقابل الـ Objectsالكلمة المفتاحية thisالـ Methodsمتغيرات الـ Instanceأساسيات الـ Constructorمراجعة - آلة حاسبة بسيطة4أمان الـ Null
مقدمة في أمان الـ NullNullable مقابل Non-Nullableعاملا الـ ? و !الكلمة المفتاحية Late وأمان الـ Nullالعوامل المدركة للـ Nullأمان الـ Null في الـ Classesمراجعة - نظام ملف تعريف المستخدم7الفئات المجردة والواجهات
الفئات المجردةالدوال المجردةالواجهات في Dartالواجهات الضمنيةالتنفيذ مقابل التوسيعالواجهات المتعددةمراجعة - حاسبة الأشكال10المجموعات والأنماط العامة
نظرة عامة على List و Set و Mapالمجموعات آمنة النوعالفئات العامةالدوال العامةقيود الأنماط العامةIterable و Iteratorملخص - التخزين العام13مفاهيم OOP المتقدمة
التركيب مقابل الوراثة (Composition vs Inheritance)دوال التوسعة (Extension Methods)الأصناف القابلة للاستدعاء (Callable Classes)الأصناف المختومة (Sealed Classes - Dart 3)السجلات (Records - Dart 3)الأنماط والمطابقة (Patterns & Matching - 3.0)التعدادات مع الدوال (Enums with Methods)2المنشئات في Dart
المنشئ الافتراضيالمنشئات المسماةقوائم التهيئةالمنشئات الثابتةالمنشئات المصنعيةالمنشئات الموجهةملخص - بناء الأشكال5التغليف (Encapsulation)
الأعضاء العامة مقابل الخاصةاتفاقية البادئة _الخصوصية على مستوى المكتبةتعمق في Getters و Settersإخفاء المعلوماتمراجعة - سجلات الطلاب8Mixins
مقدمة إلى Mixinsإنشاء Mixinsاستخدام Mixins متعددةالكلمة المفتاحية on في MixinsMixin مقابل الوراثة (Inheritance)Mixin مقابل الواجهة (Interface)مراجعة - نظام الحيوانات (Animal System)3خصائص الـ Class
أعضاء الـ Instance مقابل الـ Staticحقول Final و Constمتغيرات Lateالـ Static Methods والحقولدوال الـ Getters والـ Settersمراجعة - مدير الحساب البنكي6الوراثة
أساسيات الوراثةالكلمة المفتاحية superإعادة تعريف الدوال (Method Overriding)الوسم @overrideالكلمة المفتاحية final Classالدوال البانية والوراثةمراجعة - هيكلية الموظفين9تعدد الأشكال
أساسيات تعدد الأشكالتعدد الأشكال عبر الـ Interfacesالتحقق من النوع أثناء التشغيلالمعاملات is و asالكلمة المفتاحية Covariantملخص - معالج الدفع12البرمجة الكائنية غير المتزامنة (Async OOP)
Futures و async/awaitأساسيات الـ Streamsمتحكمات الـ Streamsالـ Constructors غير المتزامنةالعمليات غير المتزامنة في دوال الكلاسمراجعة - جالب البيانات (Data Fetcher)15أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع