Dart: عوامل تشغيل Dart

آخر تحديث: 2026-08-26

عوامل التشغيل هي أفعال الكود — أتقنها لجعل البيانات "تتحرك".

1. ما ستتعلمه


2. قصة مطور حقيقي

(1) نقطة الألم: نفس الكود، نتائج مختلفة — String + Int في JavaScript أمان، في Dart خطأ

كان Tom يكتب تطبيق ويب. في JavaScript: 1 + "2" = "12" (تسلسل). في Dart 3: 1 + "2" = خطأ في وقت الترجمة. "ما الذي يحدث؟ لماذا هو صارم جدًا؟" سأل زميله. أجاب: "هذا هو أمان Dart. في JS، أنت مسؤول عن كل تحويل نوع خفي. في Dart، المترجم يحميك."

(2) حل Dart

DART
// كود JavaScript — "يعمل" لكن به أخطاء خفية
// const result = 1 + "2";  // "12" (تسلسل!)

// ✅ Dart: يجب أن يكون النوع متطابقًا
int result = 1 + 2;        // 3
String concat = '1' + '2'; // '12' (تسلسل سلسلة سلسلة)
String fromInt = '$result' + ' items'; // '3 items' (استيفاء)

(3) النتيجة: أخطاء وقت التشغيل → منع وقت الترجمة

السيناريو JavaScript Dart 3
1 + "2" "12" (تسلسل) ❌ خطأ في وقت الترجمة
if ("hello") truthy ❌ خطأ في النوع
null.foo() TypeError في وقت التشغيل ❌ لا يمكن الوصول (نوع غير قابل للفراغ)
== null == == (متطابق)
TEXT 📖 للعرض فقط
قبل (JS): اختبار يمر، أخطاء وقت التشغيل في الإنتاج
بعد (Dart): أخطاء وقت الترجمة، نوع آمن، إنتاج خالٍ من الأخطاء
ROI: عيوب وقت التشغيل ↓70%

3. عوامل التشغيل الحسابية

(1) 6 عوامل حسابية أساسية

DART
int a = 10;
int b = 3;

print(a + b);   // 13  (جمع)
print(a - b);   // 7   (طرح)
print(a * b);   // 30  (ضرب)
print(a / b);   // 3.333333... (قسمة — int / int = double)
print(a ~/ b);  // 3   (قسمة عدد صحيح)
print(a % b);   // 1   (باقي)

print(-a);      // -10 (نفي)
print(+a);      // 10  (موجب — لا تأثير عمليًا)

(2) الفرق بين / و ~/

DART
// / — قسمة بفاصلة عائمة (دائمًا تُرجع double)
print(10 / 3);   // 3.3333333333333335
print(10 / 2);   // 5.0 (وليس 5)
print(1 / 3);    // 0.3333333333333333

// ~/ — قسمة عدد صحيح (تُرجع int)
print(10 ~/ 3);  // 3 (10 ÷ 3 = 3 مع باقي 1)
print(10 ~/ 2);  // 5
print(1 ~/ 3);   // 0 (1 ÷ 3 = 0 مع باقي 1)

// % — الباقي
print(10 % 3);   // 1
print(10 % 2);   // 0
print(1 % 3);    // 1

// ❌ JavaScript يخلط بين / و ~/
// JavaScript: 10 / 3 = 3 (قسمة عدد صحيح)
// Dart: 10 / 3 = 3.333... (قسمة بفاصلة عائمة)

(3) الزيادة والنقصان

DART
int x = 5;

// بادئة — الزيادة أولاً، ثم الإرجاع
print(++x);  // 6 (x = 6، ثم طباعة 6)
print(x);    // 6

// لاحقة — الإرجاع أولاً، ثم الزيادة
int y = 5;
print(y++);  // 5 (طباعة 5، ثم y = 6)
print(y);    // 6

// نفس الشيء للنقصان
int z = 5;
print(--z);  // 4
int w = 5;
print(w--);  // 5
print(w);    // 4

(4) الأسبقية والترابط

DART
// أسبقية المشغل (من الأعلى إلى الأدنى)
// 1. الأقواس ()
// 2. أحادي (!, -, +, ++, --)
// 3. الأس (**)
// 4. الضرب والقسمة (*, /, ~/, %)
// 5. الجمع والطرح (+, -)
// 6. العلاقات (<, >, <=, >=)
// 7. المساواة (==, !=)
// 8. AND المنطقي (&&)
// 9. OR المنطقي (||)
// 10. الإسناد (=, ??=)

// مثال
int result = 2 + 3 * 4;     // 14 (الضرب أولاً)
int result2 = (2 + 3) * 4;   // 20 (الأقواس أولاً)
int result3 = 10 - 5 - 2;   // 3 (من اليسار إلى اليمين)
int result4 = 2 ** 3;       // 8 (الأس)

(5) عوامل المقارنة العددية

DART
print(5 == 5);    // true
print(5 != 3);    // true
print(5 > 3);     // true
print(5 < 3);     // false
print(5 >= 5);    // true
print(5 <= 4);    // false

▶ مثال: آلة حاسبة بسيطة

DART
// آلة حاسبة CLI
void main(List<String> arguments) {
  if (arguments.length != 3) {
    print('الاستخدام: dart run calculator.dart <num1> <op> <num2>');
    print('العوامل: + - * / ~/ %');
    return;
  }

  final a = int.parse(arguments[0]);
  final op = arguments[1];
  final b = int.parse(arguments[2]);

  int result;
  switch (op) {
    case '+':
      result = a + b;
      break;
    case '-':
      result = a - b;
      break;
    case '*':
      result = a * b;
      break;
    case '/':
      // ملاحظة: القسمة هنا تحتاج التحقق من b != 0
      result = a ~/ b;  // ~/ للحصول على int
      break;
    case '%':
      result = a % b;
      break;
    default:
      print('عامل غير معروف: $op');
      return;
  }

  print('$a $op $b = $result');
}
BASH
$ dart run calculator.dart 10 + 5
10 + 5 = 15

$ dart run calculator.dart 17 / 5
17 / 5 = 3  (قسمة عدد صحيح)

$ dart run calculator.dart 17 * 5
17 * 5 = 85
TEXT 📖 للعرض فقط
> **الإخراج:** آلة حاسبة بسيطة — استخدم switch للتعامل مع عوامل مختلفة، واستخدم `~/` للحصول على قسمة عدد صحيح.

4. عوامل التشغيل العلائقية والمنطقية

(1) المقارنة (== !=)

DART
// أنواع مختلفة لا يمكن مقارنتها مباشرة
print(1 == 1);       // true
print(1 == '1');     // ❌ خطأ في وقت الترجمة (int != String)
print(1.0 == 1);     // true (1.0 == 1، == تتجاهل نوع الرقم)
print(0.0 / 0.0 == double.nan);  // false (NaN لا يساوي نفسه)

// == null
String? name;
print(name == null);  // true

(2) العوامل المنطقية

DART
bool a = true;
bool b = false;

// AND
print(a && b);  // false
print(true && true);  // true

// OR
print(a || b);  // true
print(false || false);  // false

// NOT
print(!a);  // false

// الاختزال — لا يستدعي المعامل الأيمن إذا كانت النتيجة معروفة
print(false && someFunction());  // someFunction() لن يُستدعى
print(true || someFunction());   // someFunction() لن يُستدعى

(3) عوامل المقارنة المتسلسلة

DART
int x = 5;

// Dart 3 — لا يدعم a < b < c
// print(1 < x < 10);  // ❌ خطأ في وقت الترجمة

// ✅ الطريقة الصحيحة
print(1 < x && x < 10);  // true

(4) مساواة الكائنات

DART
// == — مقارنة المحتوى (المساواة البنيوية)
print('hello' == 'hello');  // true
print([1, 2] == [1, 2]);   // true

// identical — مقارنة المرجع
print(identical('hello', 'hello'));  // true
print(identical([1, 2], [1, 2]));   // false (مراجع مختلفة)

// hashCode
print('hello'.hashCode);  // 674263843

(5) ?.[..] — عامل الفهرسة الآمن

DART
// للوصول الآمن إلى عناصر القائمة
List<int>? numbers = [1, 2, 3];
int? first = numbers?[0];   // 1
int? out = numbers?[10];    // null (بدلاً من IndexError)

numbers = null;
int? nullFirst = numbers?[0];  // null (آمن)

// الاستخدام العملي
Map<String, int>? ages = {'Alice': 30};
int? aliceAge = ages?['Alice'];  // 30
int? bobAge = ages?['Bob'];      // null

5. عوامل التشغيل البتية

(1) 6 عوامل بتية

DART
int a = 0b1100;  // 12
int b = 0b1010;  // 10

print(a & b);   // 0b1000 (8)   — AND
print(a | b);   // 0b1110 (14)  — OR
print(a ^ b);   // 0b0110 (6)   — XOR
print(~a);     // 0b...11110011 (-13) — NOT (متمم)

print(a << 1);  // 0b11000 (24)  — إزاحة لليسار
print(a >> 1);  // 0b0110 (6)    — إزاحة لليمين

// إزاحة لليمين غير موقعة
print(a >>> 1); // 0b0110 (6)

(2) حالات الاستخدام العملي

DART
// 1. الأعلام (flags)
const int flagRead = 0b001;     // 1
const int flagWrite = 0b010;    // 2
const int flagExecute = 0b100;  // 4

int permissions = flagRead | flagWrite;  // 0b011 (3)
print((permissions & flagRead) != 0);    // true (لديه إذن قراءة)

// 2. ضغط البيانات / حزم البروتوكول
int packed = (0xFF << 16) | (0xAB << 8) | 0x12;
int red = (packed >> 16) & 0xFF;   // 0xFF (255)
int green = (packed >> 8) & 0xFF;  // 0xAB (171)
int blue = packed & 0xFF;         // 0x12 (18)

// 3. التحقق من البت
int value = 0b1010;
print((value & 0b1000) != 0);  // true (البت 3 هو 1)

(3) أسبقية العوامل البتية

DART
// & له أسبقية أعلى من ^
int result = 0b1100 & 0b1010 ^ 0b0110;  // (0b1100 & 0b1010) ^ 0b0110
// = 0b1000 ^ 0b0110 = 0b1110 (14)

// استخدم الأقواس لتجنب الغموض
int result2 = 0b1100 & (0b1010 ^ 0b0110);  // 0b1100 & 0b1100 = 0b1100 (12)

▶ مثال: نظام أعلام الأذونات

DART
// نظام أذونات قائم على البتات
class FilePermissions {
  static const int read = 0b001;      // 1
  static const int write = 0b010;     // 2
  static const int execute = 0b100;   // 4

  int value = 0;

  // إضافة إذن
  void grant(int permission) {
    value = value | permission;
  }

  // إزالة إذن
  void revoke(int permission) {
    value = value & ~permission;
  }

  // التحقق من إذن
  bool hasPermission(int permission) {
    return (value & permission) == permission;
  }

  // التحقق من أي إذن
  bool hasAny(int permissions) {
    return (value & permissions) != 0;
  }

  @override
  String toString() {
    final perms = <String>[];
    if (hasPermission(read)) perms.add('R');
    if (hasPermission(write)) perms.add('W');
    if (hasPermission(execute)) perms.add('X');
    return perms.join('');
  }
}

void main() {
  final p = FilePermissions();
  p.grant(FilePermissions.read | FilePermissions.write);
  print(p);  // 'RW'

  p.grant(FilePermissions.execute);
  print(p);  // 'RWX'

  p.revoke(FilePermissions.write);
  print(p);  // 'RX'
}
TEXT 📖 للعرض فقط
> **الإخراج:** نظام أذونات قائم على البتات — كل علم 1 بت، باستخدام | و & و ~ للدمج والتحقق.

6. عوامل الإسناد والإسناد المركب

(1) عوامل الإسناد الأساسية

DART
int x;
x = 10;          // إسناد بسيط
x ??= 20;        // إسناد فقط إذا كانت x null (10، لم تتغير)
x = 30;          // إعادة إسناد

(2) عوامل الإسناد المركب

DART
int x = 10;
x += 5;   // x = x + 5 (15)
x -= 3;   // x = x - 3 (12)
x *= 2;   // x = x * 2 (24)
x /= 4;   // x = x / 4 (6.0)
x ~/= 2;  // x = x ~/ 2 (3)
x %= 2;   // x = x % 2 (1)
x <<= 2;  // x = x << 2 (4)
x >>= 1;  // x = x >> 1 (2)
x &= 0b11;  // x = x & 0b11 (2)
x |= 0b10;  // x = x | 0b10 (2)
x ^= 0b10;  // x = x ^ 0b10 (0)

(3) ?? — عامل الإسناد مع الفحص الفارغ

DART
String? name;

name = null;
name ??= 'default';  // null، عين 'default'
print(name);  // 'default'

name = 'Bob';
name ??= 'default';  // 'Bob'، لا تتغير
print(name);  // 'Bob'

// مثال عملي: الإعداد الافتراضي
String config = getEnvVar('API_KEY') ?? 'default-key';

(4) ?? — عامل coalesce الفارغ

DART
String? a = null;
String? b = null;
String? c = 'hello';

String result1 = a ?? b ?? c ?? 'default';  // 'hello'
String result2 = a ?? b ?? 'fallback';       // 'fallback'

// ?? مع null-aware method call
int? length = a?.length ?? 0;  // 0

(5) الفرق بين ?? و ??

DART
// ??= — إسناد فقط إذا كانت null
String? x;
x ??= 'A';  // x = 'A'
x ??= 'B';  // x = 'A' (لم تتغير)

x = null;
x ??= 'A';  // x = 'A'

// ?? — إرجاع أول قيمة غير null
String a = null;
String b = 'B';
String c = a ?? b;  // 'B'
String d = b ?? a;  // 'B'

7. عامل Cascade ..

(1) ما هو Cascade؟

Cascade .. يستدعي عدة أساليب على نفس الكائن في تعبير واحد.

DART
// ❌ بدون Cascade — متكرر
final user = User();
user.name = 'Bob';
user.age = 25;
user.email = 'bob@example.com';
user.active = true;
print(user);

// ✅ مع Cascade
final user = User()
  ..name = 'Bob'
  ..age = 25
  ..email = 'bob@example.com'
  ..active = true
  ..print();

(2) Cascade يستدعي أساليب void

DART
class Builder {
  String name = '';
  int version = 0;
  List<String> features = [];

  Builder setName(String name) { this.name = name; return this; }
  Builder setVersion(int v) { version = v; return this; }
  Builder addFeature(String f) { features.add(f); return this; }
  void build() => print('Built: $name v$version with $features');
}

// بدون Cascade
final b1 = Builder();
b1.setName('MyApp');
b1.setVersion(1);
b1.addFeature('auth');
b1.build();

// مع Cascade
final b2 = Builder()
  ..setName('MyApp')
  ..setVersion(1)
  ..addFeature('auth')
  ..build();

(3) ?.. — Cascade مع فحص الفارغ

DART
// ?.. — تنفيذ Cascade فقط إذا كان الكائن غير null
List<int>? numbers = [1, 2, 3];
numbers?..add(4)..add(5);  // [1, 2, 3, 4, 5]
print(numbers);  // [1, 2, 3, 4, 5]

numbers = null;
numbers?..add(4)..add(5);  // لا تفعل شيئًا (آمن)
print(numbers);  // null

// مثال عملي
User? user = getUser();
user?..save()..notify();  // آمن — لن يفعل شيئًا إذا كان user null

(4) Cascade على الأنواع الأخرى

DART
// على List
final list = <int>[1, 2]
  ..add(3)
  ..add(4)
  ..addAll([5, 6]);
// [1, 2, 3, 4, 5, 6]

// على Map
final map = <String, int>{}
  ..['Alice'] = 30
  ..['Bob'] = 25;
// {Alice: 30, Bob: 25}

// على File
final file = File('data.txt')
  ..createSync()
  ..writeAsStringSync('Hello');

(5) Cascade يستدعي void فقط

DART
class Counter {
  int _value = 0;
  void increment() { _value++; }
  int get value => _value;
}

final c = Counter()..increment()..increment()..increment();
print(c.value);  // 3

// ❌ Cascade لا يمكنه استدعاء دوال إرجاع
// final result = Counter()..increment().increment();  // ❌ قيمة الإرجاع لا يمكن الوصول إليها

▶ مثال: تكوين كائنات معقدة

DART
class HttpClient {
  String baseUrl = '';
  int timeout = 30;
  Map<String, String> headers = {};

  HttpClient setBaseUrl(String url) { baseUrl = url; return this; }
  HttpClient setTimeout(int s) { timeout = s; return this; }
  HttpClient addHeader(String k, String v) { headers[k] = v; return this; }
  void send() => print('POST $baseUrl (timeout=${timeout}s) headers=$headers');
}

void main() {
  // بدون Cascade — 6 أسطر
  final client1 = HttpClient();
  client1.setBaseUrl('https://api.example.com');
  client1.setTimeout(60);
  client1.addHeader('Content-Type', 'application/json');
  client1.addHeader('Authorization', 'Bearer xxx');
  client1.send();

  // مع Cascade — 6 أسطر لكن أكثر قابلية للقراءة
  final client2 = HttpClient()
    ..setBaseUrl('https://api.example.com')
    ..setTimeout(60)
    ..addHeader('Content-Type', 'application/json')
    ..addHeader('Authorization', 'Bearer xxx')
    ..send();
}
TEXT 📖 للعرض فقط
> **الإخراج:** يجعل Cascade تكوين الكائن أكثر إيجازًا وقراءة، خاصةً في الاختبارات وإعداد البيانات.

(6) cascade_aware

DART
// ؟؟ إرجاع أول قيمة غير null، مع Cascade
Map<String, dynamic>? config = null;
final url = config?['url'] ?? 'default';  // 'default'

// ❌ هذا لا يعمل — ? لا يدعم عملية الفهرسة
// print(config?[name]);  // بناء جملة غير صالح

8. عوامل تشغيل أمان القيم الفارغة

(1) ?. — الوصول الآمن

DART
String? name = 'Bob';
print(name?.length);  // 3 (آمن — يعيد null إذا كان name null)

name = null;
print(name?.length);  // null (لا يلقي NullPointerException)

// مثال متسلسل
User? user = getUser();
String? username = user?.name;  // null إذا كان user null
String? firstLetter = user?.name?[0];  // null أو الحرف الأول

(2) ?? — الإرجاع الآمن (Coalesce)

DART
String? a;
String result = a ?? 'default';  // 'default' (لأن a null)

// ✅ السلسلة
String? a;
String? b = null;
String? c = 'hello';
String result = a ?? b ?? c ?? 'default';  // 'hello'

// الاستخدام العملي
String username = getCookie() ?? getLocalUsername() ?? 'guest';

(3) ??= — إسناد آمن

DART
Map<String, int?> counts = {'a': 1, 'b': null};

counts['a'] ??= 10;  // 1 (لم تتغير)
counts['b'] ??= 20;  // 20 (تم إسناد)
counts['c'] ??= 30;  // 30 (مفتاح جديد)

// الاستخدام العملي
Map<String, String> config = {};
config['apiKey'] ??= 'default-key';
config['timeout'] ??= '30';

(4) المقارنة بين 3 عوامل

عامل سيناريو الاستخدام مثال
?. الوصول إلى الخاصية/الطريقة user?.name
?? الإرجاع مع قيمة افتراضية name ?? 'default'
??= إسناد فقط إذا كانت null name ??= 'default'

▶ مثال: تكوين آمن للقيم الفارغة

DART
class Config {
  String? apiKey;
  String? baseUrl;
  int? timeout;
  Map<String, String>? customHeaders;

  // قراءة آمنة مع قيم افتراضية
  String getApiKey() => apiKey ?? 'default-key';
  String getBaseUrl() => baseUrl ?? 'https://api.example.com';
  int getTimeout() => timeout ?? 30;

  // سلسلة الوصول
  String? getFullUrl(String? path) {
    return getBaseUrl() + (path ?? '');
  }

  // إعدادات افتراضية
  void setDefaults() {
    apiKey ??= 'default-key';
    baseUrl ??= 'https://api.example.com';
    timeout ??= 30;
  }

  @override
  String toString() {
    return 'Config{apiKey=${getApiKey()}, baseUrl=${getBaseUrl()}, timeout=${getTimeout()}s}';
  }
}

void main() {
  final c = Config();
  c.setDefaults();
  print(c);
  // Config{apiKey=default-key, baseUrl=https://api.example.com, timeout=30s}
}
TEXT 📖 للعرض فقط
> **الإخراج:** عوامل أمان القيم الفارغة (?. / ?? / ??=) تجعل الكود أكثر إيجازًا وقراءةً، مع ضمان الأمان من NullPointerException.

9. عوامل تشغيل فحص النوع

(1) is — فحص النوع

DART
Object value = 'hello';

if (value is String) {
  print('String length: ${value.length}');  // 5
  print('is String: true');
} else if (value is int) {
  print('int value');
} else {
  print('unknown type');
}

// مع التحسس الذكي للأنواع
if (value is String) {
  // داخل هذا النطاق، value مُحوّل تلقائيًا إلى String
  print(value.toUpperCase());  // 'HELLO'
}

(2) is! — فحص النوع غير المتطابق

DART
Object value = 42;

if (value is! String) {
  print('not String: true');
}

(3) as — تحويل قسري

DART
Object value = 'hello';

// تحويل قسري
String s = value as String;  // ✅
print(s.toUpperCase());  // 'HELLO'

// تحويل خاطئ
try {
  int i = value as int;  // ❌ TypeError في وقت التشغيل
} catch (e) {
  print('فشل التحويل: $e');
}

(4) as? — تحويل آمن

DART
Object value = 'hello';

String? s = value as? String;  // 'hello'
int? i = value as? int;        // null (بدلاً من رمي)

if (s != null) {
  print('String length: ${s.length}');
}

// الاستخدام العملي: معالجة Map<String, dynamic>
Map<String, dynamic> data = {'name': 'Bob', 'age': 25};
String? name = data['name'] as? String;  // 'Bob'
int? age = data['age'] as? int;          // 25
String? email = data['email'] as? String; // null (مفتاح غير موجود)

(5) switch التعبيرية + مطابقة الأنماط (Dart 3)

DART
String describe(Object obj) => switch (obj) {
  int n when n > 0 => 'int موجب $n',
  int n => 'int $n',
  String s when s.isNotEmpty => 'String غير فارغ "$s"',
  String _ => 'String فارغ',
  List l when l.isEmpty => 'قائمة فارغة',
  List l => 'قائمة بطول ${l.length}',
  _ => 'unknown',
};

print(describe(42));        // 'int موجب 42'
print(describe('hello'));  // 'String غير فارغ "hello"'
print(describe([1, 2]));   // 'قائمة بطول 2'

(6) sealed class (Dart 3)

DART
sealed class Result<T> {}
class Success<T> extends Result<T> {
  final T value;
  Success(this.value);
}
class Failure<T> extends Result<T> {
  final String error;
  Failure(this.error);
}

// switch شامل — يفرض التغطية الكاملة
String handle(Result<int> r) => switch (r) {
  Success(:final value) => 'Success: $value',
  Failure(:final error) => 'Error: $error',
};

print(handle(Success(42)));    // 'Success: 42'
print(handle(Failure('oops')));  // 'Error: oops'

▶ مثال: معالجة آمنة لأنواع متعددة

DART
// معالجة الأحداث من API
abstract class Event {}
class ClickEvent extends Event { final int x, y; ClickEvent(this.x, this.y); }
class KeyEvent extends Event { final String key; KeyEvent(this.key); }
class ResizeEvent extends Event { final int width, height; ResizeEvent(this.width, this.height); }

sealed class UIEvent extends Event {}
class UIClick extends UIEvent { final String button; UIClick(this.button); }
class UIKey extends UIEvent { final String key; UIKey(this.key); }
class UIResize extends UIEvent { final int w, h; UIResize(this.w, this.h); }

String handle(UIEvent event) => switch (event) {
  UIClick(:final button) => 'نقر: $button',
  UIKey(:final key) => 'مفتاح: $key',
  UIResize(:final w, :final h) => 'تغيير حجم: ${w}x$h',
};

print(handle(UIClick('left')));     // 'نقر: left'
print(handle(UIKey('Enter')));     // 'مفتاح: Enter'
print(handle(UIResize(800, 600)));  // 'تغيير حجم: 800x600'
TEXT 📖 للعرض فقط
> **الإخراج:** sealed class + switch التعبيرية تجعل معالجة الأحداث متعددة الأنواع آمنة ووقت الترجمة.

10. عوامل التشغيل المركبة الأخرى

(1) عامل الفهرسة []

DART
List<int> numbers = [1, 2, 3, 4, 5];
print(numbers[0]);     // 1
print(numbers[4]);     // 5

// خارج النطاق
// print(numbers[10]);  // ❌ RangeError في وقت التشغيل

// مع الفحص
if (numbers.length > 10) {
  print(numbers[10]);
}

// الفهرسة الآمنة
int? n = numbers.length > 10 ? numbers[10] : null;

(2) عامل الفهرسة في Map

DART
Map<String, int> ages = {'Alice': 30, 'Bob': 25};
print(ages['Alice']);  // 30
print(ages['Bob']);    // 25
print(ages['Charlie']); // null

// الكتابة
ages['Charlie'] = 35;  // إضافة
ages['Alice'] = 31;    // تحديث

(3) الفهرسة في الفئة المخصصة

DART
class Matrix {
  final List<List<int>> _data;
  Matrix(this._data);

  // [] العامل
  int operator [](int row, int col) => _data[row][col];

  // []= العامل
  void operator []=(int row, int col, int value) {
    _data[row][col] = value;
  }
}

final m = Matrix([
  [1, 2, 3],
  [4, 5, 6],
]);

print(m[0, 0]);  // 1
print(m[1, 2]);  // 6
m[0, 1] = 20;   // تعديل
print(m[0, 1]);  // 20

(4) spread operator ...

DART
// نشر عناصر القائمة
List<int> a = [1, 2, 3];
List<int> b = [0, ...a, 4];  // [0, 1, 2, 3, 4]

// نشر عناصر الخريطة
Map<String, int> m1 = {'a': 1, 'b': 2};
Map<String, int> m2 = {'c': 3, ...m1, 'd': 4};  // {c: 3, a: 1, b: 2, d: 4}

// spread مع null-aware
List<int>? nullable = null;
List<int> c = [0, ...?nullable, 5];  // [0, 5] (بدون خطأ إذا كان null)

// spread مع مجموعة
Set<String> s1 = {'a', 'b'};
Set<String> s2 = {'c', ...s1};  // {c, a, b}

(5) collection-if و collection-for

DART
// collection-if — شرطي في المجموعة
List<int> numbers = [
  1, 2, 3,
  if (true) 4,  // مضمن
  if (false) 5,  // غير مضمن
];

// collection-for — حلقة في المجموعة
List<int> doubled = [
  for (int n in [1, 2, 3]) n * 2,
];

// map
Map<String, int> squares = {
  for (int n in [1, 2, 3]) '$n': n * n,
};
// {'1': 1, '2': 4, '3': 9}

// يدمج
List<String> items = [
  'always',
  if (true) 'maybe',
  for (int i in [1, 2]) 'item $i',
];
// ['always', 'maybe', 'item 1', 'item 2']

▶ مثال: استخدام cascade مع null-safe

DART
class User {
  String? name;
  int? age;
  List<String>? hobbies;

  void setName(String n) { name = n; }
  void setAge(int a) { age = a; }
  void addHobby(String h) { hobbies?.add(h); }

  @override
  String toString() => 'User(name=$name, age=$age, hobbies=$hobbies)';
}

void main() {
  // ❌ بدون ?.. — يلقي إذا كان null
  // final user = User()..setName('Bob');

  // ✅ مع ?.. — آمن
  final user = User()
    ?..name = 'Bob'
    ?..setName('Bob')  // ❌ ?.. لا يعمل مع الإسناد — استخدم ?. فقط
    ?..age = 25;

  print(user);
  // User(name=Bob, age=25, hobbies=null)
}
TEXT 📖 للعرض فقط
> **الإخراج:** cascade مع ?.. آمن للكائنات القابلة للفراغ، لكن cascade لا يعمل مع الإسناد (يجب استخدام ?. للوصول).

11. ملخص نقاط المعرفة الأساسية لهذا الدرس

# المفهوم المستوى الأهمية
1 عوامل حسابية: + - * / ~/ % مبتدئ ⭐⭐⭐⭐⭐
2 ~/ و / فرق دقيق: ~/ عدد صحيح، / بفاصلة عائمة مبتدئ ⭐⭐⭐⭐
3 عوامل علائقية: == != > < >= <= مبتدئ ⭐⭐⭐⭐⭐
4 عوامل منطقية: && || ! مبتدئ ⭐⭐⭐⭐⭐
5 عوامل بتية: & | ^ ~ << >> متوسط ⭐⭐⭐
6 ?? vs ??= vs ?.: ?? إرجاع، ??= إسناد، ?. وصول متوسط ⭐⭐⭐⭐⭐
7 Cascade .. و ?.. متوسط ⭐⭐⭐⭐⭐
8 فحص النوع: is / is! / as / as? متوسط ⭐⭐⭐⭐⭐
9 Spread ... و collection-if/for متوسط ⭐⭐⭐⭐

❓ أسئلة شائعة

س ما الفرق بين ?? و ??=؟
ج ?? هو تعبير — يُرجع أول قيمة غير null. ??= هو عامل إسناد — يعين فقط إذا كان المتغير null. ?? لا يعدل، ??= يعدل.
س متى تستخدم cascade .. مقابل builder pattern؟
ج cascade .. أفضل لـ: ① تكوين كائن في خطوة واحدة ② إعداد البيانات في الاختبارات ③ إنشاء UI (مثل Flutter build). builder pattern أفضل لـ: ① منطق مُعقد ② خطوات قابلة لإعادة الاستخدام ③ التحقق من المعلمات في كل خطوة.
س لماذا لا يدعم Dart a < b < c؟
ج في Python: 1 < 2 < 3 = True. في JS: 1 < 2 < 3 = true لكن 1 < 2 > 0 = true أيضًا (لأن 2 > 0 صحيح). هذا غامض. Dart اختارت الوضوح: استخدم 1 < a && a < 3، يتجنب الغموض.
س لماذا / يُرجع double في Dart 3؟
ج في JavaScript و C: 5 / 2 = 2 (قسمة عدد صحيح). في Dart 3: 5 / 2 = 2.5 (قسمة بفاصلة عائمة). إذا كنت تريد قسمة عدد صحيح، استخدم ~/ (مثل Python). هذا أكثر توقعًا للمبتدئين.
س هل يمكن استخدام .. و ?.. معًا؟
ج نعم! .. يستدعي على كائن غير null. ?.. يستدعي فقط إذا كان غير null. في الواقع، .. ضمنيًا هو ?.. (لا رمي NullError). الفرق دقيق: مع ..، إذا كان null، لا يفعل شيئًا (آمن)؛ الفرق هو في الأسلوب.
س متى تستخدم as مقابل as?؟
ج as رمي خطأ TypeError في وقت التشغيل إذا فشل التحويل. as? يُرجع null. استخدم as فقط عندما تكون متأكدًا 100% من النوع (مثل نوعك الخاص). استخدم as? في الحالات غير المؤكدة، خاصة لمعالجة JSON أو بيانات المستخدم.

📖 ملخص


📝 تمارين

  1. أساسي (الصعوبة ⭐): أنشئ مشروع calculator_pro، واكتب دالة calculate(String op, num a, num b) التي تُرجع نتيجة العوامل الحسابية الستة (+ - * / ~/ %)، وتعامل مع القسمة على صفر (تُرجع null بدلاً من ذلك). اكتب 12 حالة اختبار في test/ (2 لكل عامل، بما في ذلك القسمة على صفر). تلميح: استخدم switch (op)، استخدم try-catch لالتقاط أخطاء القسمة على صفر، واستخدم package:test للإطار.
  2. متوسط (الصعوبة ⭐⭐): أنشئ مشروع null_safety_challenge، واكتب دالة processUser(Map<String, dynamic> data) تعالج 10 أنواع مختلفة من الحقول الاختيارية (الاسم، العمر، البريد الإلكتروني، الهاتف، العنوان، الحالة، الرصيد، تاريخ التسجيل، قائمة الاهتمامات، إعدادات الإشعارات)، كل حقل يمكن أن يكون null، أو نوع خاطئ، أو قيمة فارغة. استخدم as? و ?? و ?. للتعامل بأمان. اكتب 5 حالات اختبار: ① خريطة كاملة ② نصف الحقول null ③ أنواع خاطئة ④ جميع الحقول null ⑤ القيم الفارغة (قوائم/سلاسل فارغة). تلميح: استخدم helper function للتحقق من النوع وتمرير قيم افتراضية.
  3. تحدٍّ (الصعوبة ⭐⭐⭐): أنشئ مشروع cascade_builder، واكتب فئة QueryBuilder (محاكاة لـ SQL query builder) مع 15 طريقة تتالي (select, from, where, join, leftJoin, groupBy, orderBy, limit, offset, having, union, orWhere, andWhere, insertInto, values). كل طريقة تُرجع this (أو null للسماح بـ ?..). أنشئ bin/example.dart يوضح 3 استعلامات مختلفة باستخدام cascade: ① استعلام بسيط ..select('*')..from('users')..where('age > 18') ② استعلام معقد مع 3 جداول و 5 شروط و GROUP BY ③ استعلام null-safe ?.. للتعامل مع المستخدم الاختياري. تحقق من أن جميع الاستعلامات تُرجع String صحيح. تلميح: استخدم StringBuffer لتجميع أجزاء SQL، واستخدم late final لتهيئة التأخير.

← السابق: المتغيرات وأنواع البيانات | التالي: التحكم في التدفق →

Web-Tutorial.com

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

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

100%