المتغيرات(Variables)
المتغيرات
هي حجر الأساس للغات البرمجة. فيمكن تشبيهها بالمواد التي تحتويها حقيبة ما. حيث
أنه يمكنك وضع مادة في حقيبة ما أو إخراجها أو استبدالها بمادة أخرى. ويتم تخزين هذه المتغيرات في الذاكرة. وبعبارات أخرى يمكن القول بأن
المتغيرات تمثل مواقع في الذاكرة.
من المؤكد أن لكل إنسان مجموعة من
الصفات المرتبطة به, مثل الوزن. وكذلك الأمر بالنسبة للمتغيرات, فكل متغير يجب أن
يكون لديه نوع بيانات معين مرتبط به, وهذا النوع هو الذي يحدد القيم التي يمكن تخزنينها
في ذلك المتغير. ومترجم الc# حريص على أن لا يأخذ المتغير قيمة تكون من نوع بيانات مخالف.
والمتغيرات في لغة الc# تنصب في حالتين: مخصصة
مبدئيا (Initially Assigned) وغير مخصصة مبدئيا(Initially Unassigned). فالمتغيرات المخصصة مبدئيا تمتلك قيم معرفة, بمعنى
أنها تحصل على قيم ابتدائية افتراضية تختلف باختلاف نوع البيانات.
والمتغيرات الغير مخصصة مبدئيا لا تمتلك قيم
معرفة, أي أنها لا تحصل على قيم افتراضية.
وحتى الآن قدَمت لنا الc#
سبعة أنواع من المتغيرات, وهي: local, array, instance,
static
وأنواع المعاملات مثل: reference, output, value. وسنقوم الآن بدراسة
كل واحد منهم.
وتعتبر متغيرات الأنواع static,
arrayمخصصة مبدئيا, بعكس متغيرات بعض الأنواع مثل local التي لا يتم تخصيصها مبدئيا.
public class
z
{
public static
int a;
int b;
byte[] c;
void abc(int
d, ref int e, out int f)
{
int g;
f = 10;
}
public static
void
{
}
}
قمنا في هذا البرنامج البسيط بتضمين
جميع أنواع المتغيرات السبعة, وسنقوم الآن بجولة سريعة حول هذا البرنامج.
-
حيث يمثل المتغير a
متغيرا من النوع static نتيجة إلى الكلمة المفتاحية static.
- في حين يمثل المتغير b متغيرا من النوع instance لأنه لم يتم إنشاؤه داخل أي دالة.
-
ويمثل العنصر c[0]
عنصرا من النوع array
وذلك إذا قمنا بتهيئة المصفوفة باستخدام الكلمة المفتاحية new.
-
يتم افتراضيا تمرير المتغيرات بالقيمة(by value). لذلك فإن المتغير d يعتبر متغيرا من النوع value.
-
في حين يعتبر المتغير e متغيرا من النوع ref(reference),
-
والمتغير f
هو متغير من النوع out(output).
- ونظرا لأنه تم إنشاء المتغير g داخل دالة فإنه يسمى متغيرا محليا(local).
المتغيرات local
يتم إنشاء المتغيرات المحلية(local) ابتداء من القوس
المفتوح '{' وانتهاء بالقوس المغلق '}'. وتعتبر هذه المتغيرات
غير مخصصة مبدئيا.
public class
z
{
public static
void
{
int i;
System.Console.WriteLine(i);
}
}
Compiler Error
Form1.cs(6,34): error CS0165: Use of unassigned local variable 'i'
سبب الخطأ في هذا البرنامج هو أننا
حاولنا طباعة قيمة المتغير i قبل تخصيص قيمة له.
وكما سبق, فإن هذه المتغيرات غير مخصصة مبدئيا, أي أنه لا يتم تهيئتها بقيمة
افتراضية عند إنشائها.
public class
z
{
public static
void
{
i = 10;
int i;
}
}
Compiler Error
Form1.cs(5,9): error CS0103: The name 'i'
does not exist in the class or namespace 'z'
سيظهر هذا الخطأ في ال .NET
2005 بالصيغة التالية:
The name 'i' does not exist in the current context
لا يمكننا أن نشير إلى أي متغير قبل
أن نقوم بإنشائه. وإذا فعلنا ذلك, فإن المترجم في هذا المثال سيكون غافلا عن وجود
المتغير i عند تعريبه للسطر
الخامس, وبالتالي فإنه سيتوقف ويعرض رسالة الخطأ السابقة.
المتغيرات instance
هي متغيرات يتم إنشائها خارج جميع
الدوال الموجودة ضمن class
معين. وتبدأ دورة حياة هذه المتغيرات فقط عندما
يتم إنشاء instance (object) من ذلك ال class باستخدام الكلمة المفتاحية new, وتموت عندما ينتهي استخدام ذلك ال object, أي عند النقطة التي
يشعر فيها الc#
بأنه لن يكون هناك أي مرجع يشير إلى ذلك ال object. فالمبرمج ليست له
القدرة على قتل متغيرات النوع instance.
ويمكن حل مشكلة البرنامج السابق بجعل المتغير i
متغيرا من النوع instance, وذلك عن طريق إنشاء
ذلك المتغير خارج الدالة Main لكن داخل الفصيلة z.
public class
z
{
public static
void
{
i = 10;
}
static int
i;
}
Compiler Warning
Form1.cs(7,16): warning CS0169: The private
field 'z.i' is never used
سيظهر هذا التحذير في ال .NET
2005 بالصيغة التالية:
The
private field 'z.i' is assigned but its value is
never used
فكل الذي حصلنا عليه في هذا البرنامج
هو تحذير بسيط يخبرنا بأننا قمنا بتعريف متغير اسمه i وقمنا بتخصيص قيمة له ولكننا لم نقم باستخدامه. فتهيئة المتغير لا يعني استخدامه.
وغالبا ما يكون مجمع النفايات(garbage collector) وهو المسئول عن قتل
المتغيرات قد حدد وقت قتل تلك المتغيرات, وذلك قبل إغلاق القوس .'}' ويحصل ذلك عندما تنتهي
جميع المراجع التي تشير إلى تلك المتغيرات. وبالتالي فإن المتغيرات قد تموت موتا
مبكرا. وهذا شرعي تماما في لغة الc#.
يتم دائما إنشاء الstructure على الstack. وبالتالي تكون
دورة حياة المتغيرات instance التي يتم إنشائها
داخل structure بنفس دورة حياة ذلك
ال structure. فإذا تم تخصيص ال structure مبدئيا فإنه يتم
أيضا تخصيص المتغيرات instance مبدئيا, والعكس بالعكس.
الكلمة المحجوزة static مع متغيرات النوع instance
يتم ولادة متغيرات النوع static عند تحميل ال class وتظل على قيد الحياة
حتى يتم إيقاف تنفيذ البرنامج. وتعتبر هذه المتغيرات مخصصة مبدئيا.
1- بالنسبة للمتغيرات
class z
{
static void
{
yy a = new yy();
yy b = new yy();
a.j = 11;
System.Console.WriteLine(a.j);
System.Console.WriteLine(b.j);
yy.i =
30;
System.Console.WriteLine(yy.i);
}
}
class yy
{
public static
int i = 10;
public int
j = 10;
}
Output
11
10
30
قمنا قي هذا البرنامج بإنشاء متغيرين هما i و j. حيث أن i هو متغير static, و j
ليس static(nonstatic). فإذا أردت الوصول للمتغير i الذي هو static فاستخدم yy.i,
ولكي تصل إلى المتغير j
والذي لا يعتبر static فاستخدم a.j. ولا يمكنك أن تعكس ذلك
فتقول مثلا, a.i و yy.j لأن ذلك سيتسبب في حدوث خطأين.
فالمتغيرات static هي متغيرات تنتمي إلى
الفصائل(classes). ولهذا السبب فإنه عندما نقوم بإنشاء متغير i يكون static, فإن القضية ليست كم سيكون عدد الobjects التي سنقوم بإنشائها
من الفصيلة yy, لأنها ستكون هناك دوما قيمة واحدة فقط للمتغير i, أي أنه سيكون هنالك متغير i واحد فقط في الذاكرة.
وهذا هو السبب في أنه يمكننا الوصول إلى متغير static من خلال اسم الclass. ولكن في حالة أن
المتغير لم يكن static
مثل المتغير j فإنه يجب علينا أن
نستخدم الصيغة التي تم توضيحها سابقا, وهي أن نكتب اسم الobject ثم نتبعها بنقطة يليها اسم المتغير. ولهذا فإنه في كل مرة نقوم فيها بإنشاء كائن من الفصيلة yy فإننا نقوم بإنشاء نسخة جديدة من المتغير j في الذاكرة. ففي المثال السابق كان لدينا متغيران j في الذاكرة, واحد خاص
بالكائن a
والآخر خاص بالكائن b. وبالتالي فإن j يسمى متغير instance بخلاف المتغير i. فعندما نقوم بتغيير
قيمة المتغير j
الخاص بالكائن a
إلى 11 فإن قيمة المتغير j الخاص بالكائن b يظل كما هو 10.
class z
{
static void
{
System.Console.WriteLine(yy.i);
}
}
class yy
{
public int
i = 10;
}
Compiler Error
Form1.cs(5,34): error CS0120: An object reference
is required for the nonstatic field, method, or
property 'yy.i'
فالكلمة static تدل ضمنيا على 'الحرية'. أي أنها تدل على أنه
يمكنك الوصول إلى أي عضو أو دالة بدون إنشاء أي كائن(object).
2-
بالنسبة للدوال
class z
{
static void
{
yy a;
a = new
yy();
yy.abc();
a.pqr();
}
}
class yy
{
public void
abc()
{
System.Console.WriteLine("abc");
}
public static
void pqr()
{
System.Console.WriteLine("pqr");
}
}
Compiler Error
Form1.cs(7,9): error CS0120: An object reference is required for
the nonstatic field, method, or property 'yy.abc()'
Form1.cs(8,9): error CS0176: Static member
'yy.pqr()' cannot be accessed with an instance
reference; qualify it with a type name instead
إذن يتبين لنا من خلال هذا البرنامج
أن نفس قوانين ال static الخاصة بالمتغيرات تنطبق على الدوال أيضا. وهذا هو سبب حدوث هذين الخطأين.
class z
{
static void
{
yy a;
a = new
yy();
a.abc();
yy.pqr();
}
}
class yy
{
public void
abc()
{
System.Console.WriteLine("abc");
}
public static
void pqr()
{
System.Console.WriteLine("pqr");
}
}
Output
abc
pqr
وفي كلتا الحالتين(nonstatic,
static)
فإنه يتم استخدام النقطة(dot) للإشارة إلى المتغير أوالدالة.
والاختلاف الوحيد هو أن العضو static ينتمي إلى ال class, لذلك فنحن لسنا بحاجة
لإنشاء object حتى يمكننا الوصول إليه. والعضو nonstatic, وهذا هو الافتراضي,
يمكن الوصول إليه فقط من خلال object من ذلك الclass .
لاحظ بأن الدالة Main الموجودة في
الفصيلة z
هي static. ولهذا فإنها ليست
هنالك حاجة لإنشاء أي كائن من الفصيلة z. ونلاحظ بأن WriteLine الموجودة في ال class المسمى Console هي أيضاstatic, لذلك فإننا لم نقم بإنشاء object من ال class المسمى Console للوصول إليها.
متغيرات المصفوفات(Array)
أما بالنسبة لأعضاء المصفوفات فإنها
تتصرف بنفس الطريقة التي يتصرف بها أعضاء ال instance. وأعضاء المصفوفات يمكن تخصيصها مبدئيا.
public class
z
{
public static void
{
int[]
a=new int[5];
System.Console.WriteLine(a[0]);
}
}
Output
0
قمنا في هذا البرنامج بإنشاء مصفوفة من النوع int
حجمها 5.
وبالتالي فإنه يمكن تخزين خمس قيم فيها من النوع int. ثم قمنا بطباعة العنصر الأول للمصفوفة قبل أن
نقوم بتخصيص قيمة معينة له. ولكن مع ذلك, لم يتم توليد رسالة خطأ, لأنه وكما
أسلفنا سابقا بأنه يتم تخصيص قيم افتراضية لأعضاء المصفوفة. وهذه القيم تختلف
باختلاف نوع البيانات. وبما أن المصفوفة هنا هي من النوع int فإن
القيمة الافتراضية هي 0, الأمر الذي أدى إلى طباعة القيمة 0.
المتغيرات value
تبدأ دورة حياة معاملات النوع value عند القوس المفتوح { وتنتهي عند القوس المغلق} . ويتم تخصيص قيم لها عند استدعاء الدالة. ويتم مع هذا النوع من
المتغيرات إنشاء مواقع جديدة في الذاكرة.
class z
{
public static
void
{
int
i=100;
yy a;
a=new yy();
a.abc(i);
System.Console.WriteLine(i);
}
}
class yy
{
public void
abc(int i)
{
System.Console.WriteLine(i);
i=10;
}
}
Output
100
100
نلاحظ في هذا البرنامج أنه تم إنشاء كائن اسمه a من الفصيلة yy. وتم تهيئة المتغير i بالقيمة 100. ثم تم إنشاء الكائن a.
من ثم قمنا باستدعاء الدالة abc وتمرير المتغير i كمعامل لها. وقمنا داخل الدالة abc
بطباعة قيمة المتغير i.
ونظرا لأن المتغير i
كان يحمل القيمة 100 عند استدعاء الدالة abc من الدالة Main,
فإن قيمته في الدالة abc
ستظل أيضا 100,
ولذلك فإنه سيتم طباعة القيمة 100.
ثم قمنا بعد ذلك في الدالة abc بتخصيص قيمة جديدة للمتغير i وهي
القيمة 10.
ولكن لاحظ هنا بأن ال i
الذي سيتغير هو المعامل i في الدالة abc وليس المتغير i في الدالة Main.
وهذا يبدو كما لو كان لديك حقيبتين متشابهتين, فقمت بتغيير محتويات إحداهن وأبقيت
على الأخرى. ولذلك فإن تعليمة الطباعة في الدالة Main
ستقوم بطباعة القيمة 100 وليس 10.
أي أن المتغير i الموجود في الدالة abc هو متغير مؤقت. فهو يعيش بين قوسي الدالة
'{' و'}'
فقط. ولذلك فإن المتغير i يعتبر متغيرا محليا في الدالة abc. أي أن قيمته تكون غير متاحة في الدالة Main. إذن فإن تغيير قيمة المتغير i الموجود في الدالة abc سوف لن تؤثر على قيمة المتغير i الموجود في الدالة Main.
حاول أن لاتقم
بتسمية المتغير i في الدالة abc بالاسم i لأن ذلك سيسبب الإرباك للجميع.
المتغيران out, ref
وبعكس المتغيرات value فإن المتغيرات reference لا يتم معها إنشاء
مواقع جديدة في الذاكرة. فهي ببساطة ترمز إلى مراجع(references) لل
objects الأصلية. أي أنها تمثل
المواقع الأصلية لل objects في الذاكرة.
ويجب تخصيص قيم لمتغيرات النوع reference عند استدعاء الدالة,
بعكس المعاملات التي من النوع out. ويتشابه المعاملان out و ref
في عدة نواحي. فلا يتم معهما إنشاء مواقع جديدة في
الذاكرة, بمعنى أن كل منهما يملك نفس قيمة المتغير
الأساسي الخاص به. وبالنسبة للمتغيرات out فإنه لا يمكن أن نقوم بتهيئتها عند استدعاء الدالة,
ولكنه يجب علينا ذلك عند إنشائها. وبما أنها تكون غير مخصصة مبدئيا, فإنه لا
يمكننا قراءة قيمها في الدالة.
وفي ال structure فإنه يتم تمرير
الكلمة المفتاحية this بالمرجع(by reference). وتعتبر الكلمة المفتاحية this في دالة البناء
الخاصة ب structure معاملا من النوع out.
وإذا رجعنا قليلا
إلى البرنامج السابق. فإنه يمكن أن يؤثرتغيير قيمة i في الدالة abc على قيمة i في الدالة Main, إذا استخدامنا out أو ref. فكأنك تقول في هذه الحالة للدالة abc "سأقوم بتمرير المتغير i إليك, استخدميه كما تريدين, وقومي أيضا
بتغيير قيمته كما تشاءين".
class z
{
public static void
{
yy a;
int i =
100;
a = new
yy();
a.abc(i);
System.Console.WriteLine(i);
}
}
class yy
{
public void abc(out int i)
{
i = 10;
}
}
Compiler Error
Form1.cs(8,9): error CS1502: The best overloaded
method match for 'yy.abc(out int)'
has some invalid arguments.
Form1.cs(8,15): error CS1503: Argument '1':cannot
convert from 'int' to 'out int'.
سيظهر الخطأ الثاني في ال .NET
2005 بالصيغة التالية:
Argument '1' must be passed with the 'out' keyword
هذا
البرنامج هو نفس البرنامج السابق له, ماعدا أننا قمنا بإضافة الكلمة 'out' للمعامل i
في الدالة abc.
ولكنه تولد خطأ عند ترجمة هذا البرنامج. فلعلك تدرك بأننا كتبنا في الدالة Main التالي a.abc(i), وكتبنا في
الدالة abc
التالي
abc(out int i). إذن فإن 'out' هي المفتاح لحل مشكلتنا السابقة. حيث أن 'out' تعني بأن أي تغييرات سيتم إجرائها في
الدالة abc
سيتم إظهارها خارج تلك الدالة أيضا.
وحتى
تتخلص من ذلك الخطأ فإنه يجب عليك أن تقوم بإرفاق الكلمة 'out' للمتغير i عند استدعاء الدالة أيضا.
وسنقوم
الآن بإجراء التغييرات اللازمة على البرنامج السابق.
class z
{
public static
void
{
yy a;
int
i=100;
a=new yy();
a.abc(out
i);
System.Console.WriteLine(i);
}
}
class yy
{
public void
abc( out int i)
{
i=10;
}
}
Output
10
قمنا في هذا
البرنامج بكتابة الكلمة 'out' عند استدعاء الدالة وعند تعريف
الدالة نفسها. إذن عندما تقول a.abc(out i) فإنك بذلك تجيز
للدالة abc
أن تقوم بإجراء تغييرات على قيمة المتغير i. ولهذا فإنه سيتم طباعة القيمة الجديدة وهي 10.
سنقوم الآن
باستخدام الكلمة 'ref' بدلا من 'out'. ودعنا نرى مدى تأثير ذلك على
البرنامج.
class z
{
public static
void
{
yy a;
int
i=100;
a=new yy();
a.abc(ref
i);
System.Console.WriteLine(i);
}
}
class yy
{
public void
abc( ref int j)
{
j=10;
}
}
Output
10
يعطي هذا
البرنامج نفس خرج البرنامج السابق, حيث يتم طباعة القيمة 10. إذن فإن 'ref' تشبه 'out' من تلك الناحية. أي أن القيمة
الأصلية للمتغير i تتغير هي أيضا.
لنقم الآن
بمحاولة تنفيذ البرنامج السابق في حالة عدم تهيئة المتغير 'i'.
class z
{
public static
void
{
yy a;
int i;
a=new yy();
a.abc(ref
i);
System.Console.WriteLine(i);
}
}
class yy
{
public void
abc( ref int j)
{
j=10;
}
}
Compiler Error
Form1.cs(8,19): error CS0165: Use of unassigned
local variable 'i'
لم نقم في هذا
البرنامج بتهيئة المتغير i.
ومع استخدام 'ref' حصلنا على خطأ. فلم نقم بتهيئة المتغير i وحاولنا مع ذلك استخدامه, وهذا هو سبب
توليد الخطأ السابق.
لنقم الآن بإلقاء
نظرة على المثال التالي. حيث قمنا باستخدام نفس البرنامج السابق لكن باستخدام 'out'
بدلا من 'ref'.
class z
{
public static
void
{
yy a;
int i;
a=new yy();
a.abc(out
i);
System.Console.WriteLine(i);
}
}
class yy
{
public void
abc( out int j)
{
j=10;
}
}
Output
10
ففي هذا البرنامج
لم نقم أيضا بتهيئة المتغير i
ولكن هذه المرة لم نحصل على خطأ. وهذا يعني بأنه يجب علينا أن نقوم بتهيئة المتغير
عند استخدام 'ref' وبالتالي فإنه يمكننا تغيير قمته بعد
ذلك. ولكن في حالة 'out' فإنه لايجب عليك أن تقوم بتهيئة المتغير
لتغيير قيمته.
ولكن لماذا هذا
الفرق بينهما؟ هذا يعود إلى أنه في حالة 'ref' يمكنك قراءة قيمة المتغير بالإضافة
إلى إمكانية الكتابة عليه(تغيير قيمته). ولذلك فإنه إذا وجب على شخص ما قراءة
قيمته, فإنه يجب أن يكون قد تم تهيئته من قبل. لكن الأمر عائد إليك فيما إذا كنت
تريد تغيير قيمته أو القراءة منه فقط أو كليهما.
ولكن في حالة 'out'
فإنه يمكنك تغيير قيمته فقط, أي أنه لايمكنك القراءة منه. ولهذا السبب فإنه لايهم
إذا لم تقم بتهيئة المتغير.
سنقوم في
البرنامج التالي بتهيئة قيمة المتغير i.
لاحظ أننا نستخدم هنا 'out'.
class z
{
public static
void
{
yy a;
int
i=100;
a=new yy();
a.abc(out
i);
System.Console.WriteLine(i);
}
}
class yy
{
public void
abc( out int j)
{
System.Console.WriteLine(j);
j=10;
}
}
Compiler Error
Form1.cs(16,34):error CS0165: Use of unassigned
local variable 'j'
سيظهر هذا الخطأ في ال .NET
2005 بالصيغة التالية:
Use
of unassigned out parameter 'j'
بالنسبة للدالة abc قمنا فيها أولا بطباعة قيمة المتغير i. وهذا يعني بأننا حاولنا قراءة قيمة
المتغير i والذي تم تمريره إلى المتغير j في الدالة abc. ولكننا كما أسلفنا سابقا فإنه عند
استخدام 'out' لايمكنك قراءة القيم, وهذا هو سبب حدوث
الخطأ. وإذا قمت الآن بتنفيذ نفس هذا البرنامج لكن باستخدام 'ref' بدلا من 'out' فإنك سوف لن تستقبل أي خطأ. وهذا
وكما أسلفنا سابقا يرجع إلى أن 'ref' تمكنك من قراءة القيم بالإضافة إلى
تغييرها.
ولكن لاحظ بأن
جميع معاملات 'out' يجب أن يتم تهيئتها(تغيير قيمتها داخل
الدالة abc)
وإلا فإن المترجم سيقوم بتوليد خطأ كما يلي.
class z
{
public static void
{
yy a;
int i =
10;
a = new
yy();
a.abc(out
i);
System.Console.WriteLine(i);
}
}
class yy
{
public void abc(out int j)
{
}
}
Compiler Error
Form1.cs(14,17): error CS0177: The out parameter
'j' must be assigned to before control leaves the current method
فما الفائدة من استخدام 'out'
إذن؟ نستخدم 'out' إذا أردنا من الدالة أن تقوم بعمل بعض
العمليات الحسابية وتخزين الناتج في متغير.
class z
{
public static void
{
yy a;
int i =
10;
a = new
yy();
a.abc(out
i);
System.Console.WriteLine(i);
}
}
class yy
{
public void abc(out int i)
{
i = 20 * 20;
}
}
Output
400
استخدمنا
في هذا البرنامج المعامل 'out'. حيث قمنا بإجراء عملية حسابية وهي ضرب
العدد 20 بنفسه وحفظ الناتج في المتغير i. وبذلك فإنه تم تغيير قيمة المتغير i ومن ثم طباعة القيمة 400.
المثال
التالي سيجعل الأمور أكثر وضوحا.
class z
{
public static void
{
yy a;
int i =
10;
int k =
20;
int m;
a = new
yy();
m
= a.abc(out i, out
k);
System.Console.WriteLine(i);
System.Console.WriteLine(k);
System.Console.WriteLine(m);
}
}
class yy
{
public int abc(out int x, out int y)
{
int j;
x = 30;
y = 10;
j
= x * y;
return
j;
}
}
Output
30
10
300
بعد أن قمنا بالإعلان عن الكائن a من الفصيلة yy, قمنا بتهيئة المتغير i بالقيمة 10 والمتغير k بالقيمة 20. ثم قمنا بإنشاء الكائن a. قمنا بعد ذلك باستخدام المتغير الثالث
وهو m لخزن القيمة التي سترجعها الدالة abc. ثم قمنا باستدعاء الدالة abc بتمريرها المعاملين i و k وذلك باستخدام 'out'. وتحتوي تلك الدالة على المتغير j من النوع int. ثم قمنا بتخصيص قيمتين جديدتين للمتغيرين
x و y وهي 30 و 10 على التوالي. حيث قمنا بضرب
قيمتهما وحفظ ناتج ذلك في المتغير j.
ثم قمنا بإرجاع قيمةالمتغير j التي سيتم خزنها في المتغير m. ستقوم تعليمة الطباعة الأولى بعرض قيمة
المتغير i وهي القيمة 30. وستقوم تعليمة الطباعة الثانية
بعرض قيمة المتغير k وهي القيمة 10. وستقوم تعليمة الطباعة الثالثة
بعرض قيمة المتغير m وهي القيمة 300.
باختصار-
في كلتا الحالتين 'ref' و
'out'
سيتم تغيير قيمة المتغير الأصلي.
عندما نستخدم 'ref' فهذا يعني بأننا سنقوم بتهيئة
المتغير. حيث تتوقع الدالة abc
بأنه سيتم تهيئة المتغير حتى تتمكن تلك الدالة من استخدام تلك القيمة. وعندما يتم
تغيير قيمة ذلك المتغير يعني هذا بأنه سينعكس ذلك التغيير حتى في خارج تلك الدالة.
إذن, يجب عليك أن تقوم بتهيئة المتغير كلما استخدمت 'ref', وإلا فإنه سيتم إعلامك بوجود خطأ.
حيث ستفترض الدالة بأنه فعلا تم تهيئة المتغير لأنها تتوقع بأن تقرأ قيمته. ولكن
الأمر عائد إليك في قراءة القيمة فقط أو تغييرها أو قرائتها وتغييرها معا.
ولكن عندما تستخدم 'out'
فهذا يدل ضمنيا على أننا لن نقوم بتهيئة المتغير, وذلك لأن الدالة abc لاتتوقع بأنها ستقوم باستخدام تلك
القيمة, فهي تتوقع فقط بأنها ستقوم بتغييرها فقط. فعند استخدام 'out'
يمكن للدالة abc
أن تقوم باستخدام المتغير حتى لو لم يتم تهيئته. وهذا يرجع إلى أنه لايمكنك قراءة
القيمة في حالة المتغير 'out'؛ فالدالة لايمكنها سوى أن تقوم
بتغيير القيمة. وبعبارات أخرى, فإنه يجب عليها أن تقوم بتغيير قيمة ذلك المتغير
وذلك لتفادي أي خطأ.