المتغيّر موضع تخزين مسمّى بنوع ثابت. في C# تصرّح به بنوع واسم، ويمكنك اختياريًا إعطاؤه قيمة في العبارة نفسها. لا يتغيّر النوع بعد ذلك أبدًا: متغيّر int يحمل أعدادًا صحيحة ما دام موجودًا.
المخرجات:
Priya bought 5 items for 20.25
10
إسناد قيمة من نوع خاطئ خطأ ترجمة: تفشل quantity = "five"; بالخطأ CS0029 (cannot implicitly convert type 'string' to 'int'). التحويل بين الأنواع صريح؛ راجع تحويل الأنواع.
var: ترك المترجم يستنتج النوع
تصرّح var بمتغيّر محلي يُستنتج نوعه من مهيّئه. ليست أنواعًا ديناميكية: النوع ثابت وقت الترجمة، تمامًا كما لو كتبته بنفسك.
المخرجات:
String
Int32
Decimal
Double
List`1
لـ var ثلاث قواعد. لا تعمل إلا للمتغيّرات المحلية (لا للحقول ولا للمعاملات). ويجب تهيئة المتغيّر في التصريح: var x; هو الخطأ CS0818، "Implicitly-typed variables must be initialized". ويجب أن يكون للمهيّئ نوع: var nothing = null; لا تُترجم، لأن null وحدها بلا نوع.
متى تستخدمها مسألة أسلوب يتّفق عليه الفريق. الإرشاد المعتاد: استخدم var حين يكون النوع واضحًا من الطرف الثاني للإسناد، واكتب النوع حين لا يكون كذلك:
var orders = new List<Order>(); // obvious: var is fine
var reader = new StreamReader(path); // obvious
decimal discount = CalculateDiscount(); // not obvious: name the type
int count = items.Count(); // makes the intent clear
فخّ خفي: var total = 0; تجعل total من نوع int، فتفشل إضافة 2.5 إليه لاحقًا، وvar price = 10; من نوع int لا decimal. حين لا يكون نوع القيمة الحرفية هو ما تريده، اكتب النوع أو أضف لاحقة (10m و0.0).
النطاق
يوجد المتغيّر المحلي من تصريحه حتى نهاية الكتلة { } التي صُرّح به فيها. المتغيّرات المصرّح بها في ترويسة حلقة أو في جسم if غير مرئية بعدها.
المخرجات:
high
240
بخلاف C وC++، لا تسمح C# لكتلة داخلية بالتصريح بمتغيّر محلي يحمل اسم متغيّر في كتلة محيطة (الخطأ CS0136): يجب أن يعني الاسم شيئًا واحدًا في الدالة كلها. يمكن لكتلتين متجاورتين إعادة استخدام اسم، كما تفعل حلقتا for منفصلتان تصرّح كل منهما بـ i.
الإسناد المؤكّد
لا قيمة افتراضية للمتغيّرات المحلية. يتتبّع المترجم كل مسار في الدالة ويرفض الترجمة إن كان متغيّر قد يُقرأ قبل الإسناد إليه:
int discount;
if (isMember)
{
discount = 10;
}
Console.WriteLine(discount);
// error CS0165: Use of unassigned local variable 'discount'
أصلحه بالتهيئة عند التصريح (int discount = 0;) أو بالإسناد في كل فرع (أضف else). الحقول وعناصر المصفوفات مختلفة: تُضبط تلقائيًا على القيمة الافتراضية لنوعها (0 وfalse وnull).
const: ثوابت وقت الترجمة
const قيمة تُحدَّد وقت الترجمة. يجب تهيئتها بتعبير ثابت (قيمة حرفية، أو const آخر، أو عمليات حسابية عليهما)، ولا يمكن أن تكون إلا رقمًا أو char أو bool أو string أو تعدادًا أو null.
المخرجات:
Price in EUR
120.00 EUR
350
حقل const ساكن ضمنيًا، فيُوصل إليه عبر اسم النوع (Pricing.VatRate)، وكتابة static const خطأ. ينسخ المترجم القيمة في كل مكان يستخدمها، ولهذا لا تكلّف الثوابت شيئًا وقت التشغيل، ولهذا أيضًا يجب أن يكون const العام في مكتبة قيمة لن تتغيّر أبدًا (مثل عدد أيام الأسبوع).
readonly وstatic readonly
لا يمكن الإسناد إلى حقل readonly إلا في تصريحه أو في مُنشئ صنفه. بعد الإنشاء لا يمكن أن يتغيّر. بخلاف const تُحسب قيمته وقت التشغيل، ويمكن أن يكون من أي نوع، ويمكن أن يختلف بين الكائنات.
المخرجات:
ACC-17 opened 2024-05-02
Launch: 2020-01-15
basic, pro
تُظهر الأسطر الأخيرة سوء الفهم الشائع: تحمي readonly الحقل لا الكائن الذي يشير إليه. سيشير Plans دائمًا إلى المصفوفة نفسها، لكن عناصر المصفوفة ما زالت قابلة للتغيير. للمجموعات غير القابلة للتغيير حقًا استخدم ReadOnlyCollection<T> أو ImmutableArray<T>.
ملخّص الثلاثة:
const | readonly | static readonly | |
|---|---|---|---|
| متى تُعرف القيمة | وقت الترجمة | وقت التشغيل | وقت التشغيل |
| الأنواع المسموحة | الأرقام، char، bool، string، التعدادات، null | أي نوع | أي نوع |
| أين تُضبط | في التصريح فقط | في التصريح أو المُنشئ | في التصريح أو المُنشئ الساكن |
| لكل كائن أم مشتركة | مشتركة (ساكنة ضمنيًا) | لكل كائن | مشتركة |
| المتغيّرات المحلية | نعم (const int x = 1;) | لا | لا |
أعراف التسمية
الأسماء في C# حساسة لحالة الأحرف (total وTotal متغيّران مختلفان) ويجب أن تبدأ بحرف أو شرطة سفلية. أعراف Microsoft، التي تتبعها كل شيفرات C# تقريبًا:
- المتغيّرات المحلية والمعاملات: camelCase (
orderCountوunitPrice). - الثوابت والخصائص والدوال والأصناف والحقول العامة: PascalCase (
MaxItemsوVatRate). - الحقول الخاصة: _camelCase بشرطة سفلية في البداية (
_balance)، وهو عُرف منتشر في شيفرة .NET. - القيم المنطقية تُقرأ كأسئلة:
isActiveوhasDiscountوcanRetry. - تجنّب الاختصارات والبادئات المجرية (
strNameوiCount).
لا يمكن أن تكون كلمة محجوزة في C# اسمًا، إلا إذا سبقتها بـ @: var @class = "5B"; صحيحة، وهذا مفيد أحيانًا للشيفرة المولَّدة أو لأسماء حقول JSON.
التبديل والإسناد المتعدد
منذ C# 7 تتيح الصفوف (tuples) الإسناد إلى عدة متغيّرات في عبارة واحدة، وهذا يعطي تبديلًا في سطر واحد:
المخرجات:
20 10
Lisbon: 545,000
الشكل الثاني تفكيك: يصرّح بـ city كـ string وبـ population كـ int من صف. المزيد في الصفوف.
الأسئلة الشائعة
ما هي var في C#؟
تطلب var من المترجم استنتاج نوع المتغيّر المحلي من القيمة التي يُهيّأ بها: var count = 5; تصرّح بمتغيّر من نوع int. يبقى المتغيّر ثابت النوع ولا يمكنه لاحقًا أن يحمل نصًا. ولا تعمل var إلا للمتغيّرات المحلية التي تُهيّأ في العبارة نفسها.
ما الفرق بين const وreadonly في C#؟
const ثابت وقت الترجمة: يجب أن تكون قيمته معروفة عند الترجمة (أرقام، نصوص، bool، null)، وهو ساكن ضمنيًا، وتُنسخ القيمة في كل مكان يستخدمه. أما الحقل readonly فيُضبط مرة واحدة وقت التشغيل، في تصريحه أو في مُنشئ، ويمكن أن يحمل أي نوع، ويمكن أن يختلف من كائن لآخر.
متى أستخدم static readonly بدل const؟
استخدم static readonly لقيمة مشتركة لا يمكن أن تكون ثابتًا وقت الترجمة، مثل DateTime أو مصفوفة أو كائن، أو لقيمة عامة قد تتغيّر بين إصدارات مكتبة. ولأن قيم const تُنسخ في التجميعات المستدعية، فإن تغيير const عام يتطلب إعادة ترجمة كل تجميع يستخدمه.
ماذا يعني الخطأ "use of unassigned local variable" في C#؟
الخطأ CS0165: وجد المترجم مسارًا يُقرأ فيه متغيّر محلي قبل إسناد أي قيمة إليه. لا قيمة افتراضية للمتغيّرات المحلية في C#. هيّئ المتغيّر عند التصريح به، أو تأكّد أن كل فرع من فروع if/switch يسند إليه قبل استخدامه.
هل أستخدم var أم الأنواع الصريحة في C#؟
كلاهما سليم؛ والفرق تختار أسلوبًا. قاعدة شائعة: استخدم var حين يكون النوع واضحًا من الطرف الثاني للإسناد (var list = new List<string>();) واكتب النوع حين لا يكون كذلك (decimal total = GetTotal();). تتبع شيفرة وقت تشغيل .NET هذه القاعدة تقريبًا.