أساسيات برمجة Dart

المتغيرات وأنواع البيانات

30 دقيقة الدرس 2 من 6

ما هي المتغيرات؟

المتغير هو حاوية مسماة تخزن قيمة في ذاكرة برنامجك. فكر فيه كصندوق مُعنون حيث يمكنك وضع البيانات واسترجاعها لاحقاً وحتى تغيير ما بداخله. في Dart كل متغير له نوع يحدد نوع البيانات التي يمكنه حملها.

إعلان متغير

void main() {
  String name = 'إدريس';
  int age = 28;
  double height = 1.78;
  bool isStudent = true;

  print(name);    // إدريس
  print(age);     // 28
  print(height);  // 1.78
  print(isStudent); // true
}

تحديد الأنواع مقابل استدلال الأنواع

Dart هي لغة ذات أنواع ثابتة مما يعني أن كل متغير له نوع. لكنك لا تحتاج دائماً لكتابة النوع صراحة. يمكن لـ Dart استدلال النوع من القيمة التي تسندها.

تحديد النوع الصريح

تكتب النوع بنفسك قبل اسم المتغير:

الأنواع الصريحة

String city = 'الرياض';
int population = 7500000;
double area = 1798.0;
bool isCapital = true;

استدلال الأنواع مع var

كلمة var تتيح لـ Dart معرفة النوع تلقائياً من القيمة المسندة. بمجرد الإسناد يتم تثبيت النوع ولا يمكن تغييره.

استخدام var

var name = 'أحمد';        // Dart يستدل String
var age = 25;              // Dart يستدل int
var score = 95.5;          // Dart يستدل double
var passed = true;         // Dart يستدل bool

// name = 42;  // خطأ! name هو String لا يمكن إسناد int
ملاحظة: رغم أن var لا يُظهر النوع إلا أن المتغير لا يزال ذا نوع ثابت. يستدل Dart النوع في وقت الترجمة وليس وقت التشغيل. هذا يختلف عن اللغات ذات الأنواع الديناميكية مثل JavaScript.

كلمة dynamic

إذا أردت فعلاً متغيراً يمكنه حمل أي نوع وتغيير الأنواع لاحقاً استخدم dynamic. لكن هذا غير مستحسن عموماً لأنك تفقد أمان الأنواع.

استخدام dynamic

dynamic anything = 'مرحبا';
print(anything);        // مرحبا
print(anything.runtimeType); // String

anything = 42;
print(anything);        // 42
print(anything.runtimeType); // int

anything = true;
print(anything);        // true
print(anything.runtimeType); // bool
تحذير: تجنب استخدام dynamic إلا عند الضرورة القصوى. إنها تتجاوز نظام أنواع Dart مما يعني أن الأخطاء التي كان يمكن اكتشافها في وقت الترجمة ستظهر فقط في وقت التشغيل. استخدم أنواعاً محددة أو var بدلاً من ذلك.

أنواع البيانات الأساسية في Dart

تمتلك Dart عدة أنواع بيانات مدمجة. إليك أهمها:

1. int -- الأعداد الصحيحة

الأعداد الصحيحة هي أرقام بدون فاصلة عشرية. يمكن أن تكون موجبة أو سالبة أو صفر.

أمثلة على الأعداد الصحيحة

int age = 30;
int temperature = -5;
int hexValue = 0xFF;       // 255 بالست عشري
int bigNumber = 1000000;

print(age);         // 30
print(hexValue);    // 255

2. double -- الأعداد العشرية

الأعداد العشرية هي أرقام ذات فاصلة عشرية تُستخدم للقيم الكسرية.

أمثلة على الأعداد العشرية

double pi = 3.14159;
double price = 29.99;
double negative = -0.5;
double scientific = 1.5e3;  // 1500.0

print(pi);          // 3.14159
print(scientific);  // 1500.0

3. num -- رقم (int أو double)

النوع num هو النوع الأب لكل من int و double. استخدمه عندما يمكن أن يكون المتغير أياً منهما.

استخدام num

num score = 100;     // قيمة int
score = 99.5;        // الآن قيمة double -- كلاهما صالح

print(score);        // 99.5

4. String -- النصوص

النصوص تمثل البيانات النصية. يمكنك استخدام علامات اقتباس مفردة أو مزدوجة.

أمثلة على النصوص

String firstName = 'إدريس';
String lastName = "صالح";
String fullName = '$firstName $lastName';

print(fullName);  // إدريس صالح

5. bool -- صح أو خطأ

المتغيرات المنطقية تحمل قيمتين فقط: true أو false. تُستخدم للشروط والمنطق.

أمثلة منطقية

bool isLoggedIn = false;
bool hasPermission = true;
bool isAdult = (age >= 18);  // true إذا كان العمر 18 أو أكثر

print(isAdult);  // true

6. List -- مجموعة مرتبة

القوائم تحمل مجموعة مرتبة من العناصر (مثل المصفوفات في اللغات الأخرى).

معاينة القوائم

List<String> fruits = ['تفاح', 'موز', 'برتقال'];
List<int> numbers = [1, 2, 3, 4, 5];

print(fruits[0]);      // تفاح
print(numbers.length); // 5

7. Map -- أزواج مفتاح-قيمة

الخرائط تخزن البيانات كأزواج مفتاح-قيمة (مثل القواميس أو الكائنات في اللغات الأخرى).

معاينة الخرائط

Map<String, int> ages = {
  'أحمد': 25,
  'سارة': 30,
  'علي': 22,
};

print(ages['أحمد']); // 25
نصيحة: سنغطي القوائم والخرائط والمجموعات بالتفصيل في دروس مخصصة لاحقاً. الآن فقط اعرف أنها موجودة كأنواع أساسية في Dart.

الثوابت: final و const

عندما لا يجب أن تتغير القيمة بعد تعيينها استخدم final أو const.

final -- تُعيّن مرة واحدة في وقت التشغيل

متغير final يمكن تعيينه مرة واحدة فقط. قيمته تُحدد في وقت التشغيل.

استخدام final

final String name = 'إدريس';
// name = 'أحمد';  // خطأ! لا يمكن تغيير متغير final

final now = DateTime.now();  // القيمة تُحدد في وقت التشغيل
print(now);

const -- ثابت وقت الترجمة

متغير const يجب أن يكون معروفاً في وقت الترجمة. إنه أكثر تقييداً من final.

استخدام const

const double pi = 3.14159;
const int maxRetries = 3;
// const now = DateTime.now();  // خطأ! ليست ثابت وقت ترجمة

print(pi);         // 3.14159
print(maxRetries); // 3
ملاحظة: استخدم const عندما تكون القيمة ثابتة حقاً ومعروفة قبل تشغيل البرنامج (مثل pi أو قيم التكوين). استخدم final عندما تُعيّن القيمة مرة واحدة لكنها تُحدد في وقت التشغيل (مثل التاريخ الحالي أو مدخلات المستخدم).

المتغيرات المتأخرة

كلمة late تخبر Dart أن المتغير سيتم تهيئته لاحقاً قبل استخدامه. هذا مفيد عندما لا تستطيع إسناد قيمة عند الإعلان.

استخدام late

late String description;

void main() {
  // بعض المنطق أولاً...
  description = 'تم التعيين لاحقاً';
  print(description); // تم التعيين لاحقاً
}
خطأ شائع: إذا وصلت إلى متغير late قبل تهيئته سيطرح Dart خطأ LateInitializationError في وقت التشغيل. تأكد دائماً من إسناد قيمة قبل القراءة.

فحص الأنواع والتحويل

توفر Dart عوامل لفحص وتحويل الأنواع في وقت التشغيل:

فحص الأنواع مع is و as

void main() {
  var value = 42;

  // فحص النوع مع 'is'
  if (value is int) {
    print('إنه عدد صحيح!');  // هذا يُطبع
  }

  if (value is! String) {
    print('إنه ليس نصاً!'); // هذا يُطبع
  }

  // تحويل النوع مع 'as'
  num number = 3.14;
  double decimal = number as double;
  print(decimal); // 3.14
}

ملخص

إليك مرجعاً سريعاً لما غطيناه:

  • var -- نوع مُستدل يتم تثبيته بعد الإسناد
  • dynamic -- يمكن تغيير الأنواع (تجنبه قدر الإمكان)
  • int و double و num -- الأرقام
  • String -- النصوص
  • bool -- صح/خطأ
  • List و Map -- المجموعات (تُغطى لاحقاً بالتفصيل)
  • final -- تُعيّن مرة واحدة في وقت التشغيل
  • const -- ثابت وقت الترجمة
  • late -- تُهيأ لاحقاً قبل الاستخدام
  • is / as -- فحص وتحويل الأنواع

تمرين عملي

افتح DartPad وأنشئ برنامجاً صغيراً يُعلن عن متغيرات لملف شخصي: الاسم (String) والعمر (int) والطول (double) والحالة (bool). استخدم كلاً من الأنواع الصريحة و var. جرب إنشاء متغير final لتاريخ التسجيل وثابت const للحد الأقصى المسموح للعمر. اطبع جميع القيم باستخدام استيفاء النصوص.