المتغيرات وأنواع البيانات
ما هي المتغيرات؟
المتغير هو حاوية مسماة تخزن قيمة في ذاكرة برنامجك. فكر فيه كصندوق مُعنون حيث يمكنك وضع البيانات واسترجاعها لاحقاً وحتى تغيير ما بداخله. في 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
الثوابت: 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 للحد الأقصى المسموح للعمر. اطبع جميع القيم باستخدام استيفاء النصوص.