🔹 Null Safety وعلامة التعجب !
في Dart 🚀
في Dart Null Safety، يتم استخدام علامة التعجب !
(وتسمى Null Assertion Operator) لإخبار المترجم أن المتغير لن يكون null
، وبالتالي، يمكن التعامل معه على أنه غير قابل لأن يكون null
.
⚠️ يجب الحذر عند استخدامها! إذا كان المتغير null
بالفعل، فسيؤدي ذلك إلى خطأ وقت التشغيل (Null Pointer Exception
).
🔹 استخدام !
مع المتغيرات Nullable
عند وجود متغير nullable
(int?
، String?
، إلخ)، إذا كنت متأكدًا 100% من أنه لن يكون null
عند استخدامه، يمكنك استخدام !
لتحويله إلى non-nullable
.
📌 مثال: بدون !
dartنسختحريرvoid main() {
String? name;
print(name.length); // ❌ خطأ: name يمكن أن يكون null
}
🔹 التفسير:
- لا يمكننا استدعاء
length
مباشرة لأنname
من النوعString?
، والذي يمكن أن يكونnull
.
📌 الحل باستخدام !
dartنسختحريرvoid main() {
String? name = "Dart";
print(name!.length); // ✅ Output: 4
}
🔹 التفسير:
name!
تخبر Dart بأنname
لن يكونnull
، لذا يمكن استدعاءlength
بأمان.
🔹 مشكلة !
إذا كان المتغير null
⚠️ إذا استخدمت !
على متغير يحتوي على null
، فسيؤدي ذلك إلى خطأ وقت التشغيل.
dartنسختحريرvoid main() {
String? name;
print(name!.length); // ❌ خطأ: Unhandled exception
}
❌ التفسير:
name!
تقول لـ Dart أنname
ليسnull
، ولكن في الحقيقة هوnull
، مما يسبب خطأ تنفيذ (Unhandled Exception
).
✅ الحل: تأكد من أن المتغير ليس null
قبل استخدام !
:
dartنسختحريرvoid main() {
String? name;
if (name != null) {
print(name.length); // ✅ يعمل بدون مشاكل
} else {
print("Name is null");
}
}
🔹 استخدام !
مع List
و Map
📌 مثال مع List
dartنسختحريرvoid main() {
List<int?> numbers = [10, null, 30];
print(numbers[0]! + numbers[2]!); // ✅ Output: 40
}
🔹 التفسير:
- استخدمنا
!
للتأكد من أن القيم داخل القائمة ليستnull
قبل إجراء العمليات الحسابية.
⚠️ لكن لو حاولنا استخدام !
على null
، سيحدث خطأ:
dartنسختحريرvoid main() {
List<int?> numbers = [null, 20];
print(numbers[0]!); // ❌ خطأ: Unhandled Exception
}
✅ الحل: استخدم ?.
أو ??
:
dartنسختحريرvoid main() {
List<int?> numbers = [null, 20];
print(numbers[0] ?? "Value is null"); // ✅ Output: Value is null
}
📌 مثال مع Map
dartنسختحريرvoid main() {
Map<String, String?> user = {
"name": "Ali",
"email": null,
};
print(user["name"]!.toUpperCase()); // ✅ Output: ALI
print(user["email"]!.toUpperCase()); // ❌ خطأ: لأن القيمة `null`
}
✅ الحل: استخدم ??
بدلًا من !
:
dartنسختحريرvoid main() {
Map<String, String?> user = {
"name": "Ali",
"email": null,
};
print(user["email"] ?? "No email provided"); // ✅ Output: No email provided
}
🔹 !
مع دوال تُرجع nullable
إذا كانت دالة تُرجع null
أو قيمة (nullable type
)، يمكنك استخدام !
إذا كنت متأكدًا من أنها لن تُرجع null
.
📌 مثال مع nullable function
dartنسختحريرString? getUsername(bool isValid) {
return isValid ? "DartUser" : null;
}
void main() {
String username = getUsername(true)!;
print(username); // ✅ Output: DartUser
}
🔹 لكن إذا أرجعت null
، سيحدث خطأ:
dartنسختحريرvoid main() {
String username = getUsername(false)!; // ❌ خطأ: لأنه `null`
print(username);
}
✅ الحل: استخدم ??
لتوفير قيمة بديلة:
dartنسختحريرvoid main() {
String username = getUsername(false) ?? "Guest";
print(username); // ✅ Output: Guest
}
🔹 متى تستخدم !
ومتى تتجنبها؟
الحالة | هل يجب استخدام ! ؟ | البديل |
---|---|---|
إذا كنت متأكدًا 100% أن المتغير ليس null | ✅ نعم | لا يوجد بديل |
إذا كنت لست متأكدًا مما إذا كان المتغير null | ❌ لا | استخدم ?. أو ?? |
إذا كنت تتعامل مع دالة تُرجع nullable | ❌ لا | استخدم ?? لتوفير قيمة افتراضية |
🎯 خلاصة !
في Null Safety
✅ !
يستخدم لإجبار المتغيرات nullable
على أن تكون non-nullable
، ولكن يجب التأكد من أنها ليست null
فعليًا.
✅ إذا كان هناك احتمال أن يكون null
، استخدم ?.
أو ??
لتجنب الأخطاء.
✅ لا تفرط في استخدام !
لأنه قد يؤدي إلى Null Pointer Exception
.