مدخل إلى سي++.نيت
كلنا يعلم أن لغة سي++ من اللغات التي اتنشرت وأثبتت جدارتها بقوة خلال السنوات الماضية. لذلك كان من الضروري تطوير السي++ حتى تلائم تقنية دوت-نيت . لكن الأمر ليس بهذه السهولة. طبعا طبيعة لغة السي++ مختلفة بعض الشئ عن ال Common Language Runtime أو CLR التي تعرف دوت-نيت . بين الاثنتين يوجد تشابهات عديدة – طبعا الاثنتين هما OOP – ولكن بالرغم من هذا توجد اختلافات أيضا . على سبيل المثال : السي++ تدعم الوراثة المتعددة للأصناف classes ، أي امكانية أن يرث صنف ما من أكثر من صنف واحد ، ولكن ال CLR تمنع هذا.
من أجل هذا كان الحل الأمثل لميكروسوفت هو بناء لغة جديدة تعتمد على تركيب السي القواعدي وتشبه السي في كلماتها المحجوزة لكنها تدعم بيئة الدوت-نيت بشكل كامل . طبعا هذه اللغة هي سي# .
فجوال بيسك 6 تختلف نهائيا عن بنية ال CLR ، ولكن الفجوال بيسك هي من ملك ميكروسووفت . فباستطاعة ميكروسوفت أن تلعب بها كما تريد . لذلك تم بناء فجوال بيسك.نيت من أجل أن تدعم ال CLR . حتى أنه بات الاختلاف بينها وبين السي# فقط في التركيب اللغوي وأي شيء تفعله في سي# ممكن أن تفعله في فجوال بيسك.نيت تقريبا . الأمور التي تتفوق سي# بها عن فجوال بيسك.نيت هي أنه ممكن أن تكتب كود غير آمن في سي# وممكن أن تحمل العوامل operator overloading ولكنك لا يمكن أن تعمل هذا في فجوال بيسك.نيت .
على النقيد الآخر فإن ميكروسوفت لا تملك سي++ . لذلك ليس بامكانها أن تعدل باللغة تفسها . أيضا عدم امكانية بناء تطبيقات دوت-نيت باستخدام سي++ أمر غير مستحب لكثير من المطوريين . فما هو الحل؟
الحل الذي توصلت له ميكروسوفت هو وضع اضافات للغة تفسها ك extensions . بمعنى صحيح تسمى هذه الاضافات ب managed extensions for c++ أو فقط نقول managed c++ . لغة سي++ ليست من اللغات السهلة حتى نبدأ بتعلمها وطبعا وضع هذه الاضافات يزيد من الصعوبة طاقين . سوف نتطرق الآن إلى هذه الاضافات التي طرحت على لغة سي++ المنظمة أو Managed C++ .
من أهم الاضافات التي طرحت على اللغة هي مجموعة من الكلمات المحجوزة التي تبدء بشرطتين سفليات . طبعا يقول مقياس السي++ أنه ممكن أي مترجم أن يضيف اضافات بهذه الطريقة . وبالتالي قامت ميكروسوفت باضافة الكلمات المحجوزة التالية :
__gc : تعني أن النوع هو عرضة لمجمع البقايا (Garbage Collector) . وهي أيضا تعني أن النوع المعرف هو من نوع CTS Reference . وممكن اضافة هذه الخاصية على ال class و array والأنواع الأخرى .
__value : تعني أن النوع المحجوز ليس عرضة لمجمع البقايا وهو من نوع Value Type .
__interface : تستعمل لتعريف نوع CTS Inteface .
__box : هذه العملية تستخدم من أجل تحويل متحول من نوع value إلى نوع reference .
__unbox : هذه العملية تقوم بتحويل القيم المغلفة أو boxed إلى نوعها الأصلي . أي عكس العملية __box .
__delegate : تستخدم لتعريف CTS Delegate . وهي من أجل تعريف مؤشر إلى تابع أو callback function .
الآن دعنا نعطي مثال بسيط على برنامج مكتوب بـسي# وأشرحه ومن ثم أعطي نفس المثال بـ Managed C++ ونرى الفرق بين اللغتين .
بالسي#
interface IMath
{
int Factorial(int f);
double SquareRoot(double s);
}
class Compute : IMath
{
public int Factorial(int f)
{
int i;
int result = 1;
for (i = 2; i <= f; i++)
result = result * i;
return result;
}
public double SquareRoot(double f)
{
return System.Math.Sqrt(s);
}
}
class DisplayValues
{
static void main()
{
Compute c = new Compute();
int v;
v = 5;
System.Console.WriteLine(
“{0} factorial: {1}”,
v, c.Factorial(v));
System.Console.WriteLine(
“Square root of {0}: {1:f4}”,
v, c.SquareRoot(v));
}
}يبدأ المثال بتعريف واجهة اسمها IMath تقوم بتعريف تابعين . ثم بعدها نقوم بتعريف class يرث الواجهة السابقة (ملاحظة : في السي# ممكن الوراثة من أكثر من واجهة) . ثم في الكلاس الأخير نقوم بكتابة كود كلا من التابعين . الكلاس الأخير هو كلاس البرنامج ويحتوي على التابع main من نوع static طبعا . في التابع main نقوم ببناء متحول c من نوع Compute ونحجزه بالheap لأنه كلاس والclasses في سي# كلها من نوع Reference أي يجب أن تكون بالheap . بعدها قمنا باستدعاء WriteLine التي تنتمي للمجال Console التابع لل System . وهي وظيفتها طباعة معلومات بسيطة على نافذة ال console . طبعا {0} تعني طباعة البارامتر الأول و {1} تعني طباعة التاني . البرنامج السابق سوف يطبع التالي :
5 factorial: 120 Square root of 5: 2.2361
والنتيجة بديهية . لاحظ أن {1:f4} تعني طباعة 4 أرقام بعد الفاصلة في الرقم الحقيقي . ليس من الداعي تفرغة الذاكرة في البيئة المنظمة لأن مجمع الفضلات سوف يتولى بازالة أي من الكائنات الغير مستخدمة .
لو كتبنا نفس المثال السابق في ال Managed C++ لوجدناه كما يلي :
#include “stdafx.h”
#using
__gc __interface IMath
{
int Factorial(int f);
double SquareRoot(double s);
}
__gc class Compute : IMath
{
public: int Factorial(int f)
{
int i;
int result = 1;
for (i = 2; i <= f; i++)
result = result * i;
return result;
}
public: double SquareRoot(double f)
{
return System::Math::Sqrt(s);
}
}
void main(void)
{
Compute *c = new Compute;
int v;
v = 5;
System::Console::WriteLine(
“{0} factorial: {1}”,
__box(v), __box(c.Factorial(v)));
System.Console.WriteLine(
“Square root of {0}: {1:f4}”,
__box(v), __box(c.SquareRoot(v)));
}لو قارنة البرنامج الأخير مع برنامج السي# لوجدته متشابه إلى حد بعيد مع بعض الاضافات في كود السي++ . أولا سوف تلاحظ في السي++ استخدمنا عبارات ال #include و ال #using العادية من أجل تعريف المكتبة وهي ضرورية من أجل كتابة كود managed في سي++ . ثم قمنا بتعريف الواجهة IMath تماما كما بالسابق لكن هنا استخدمنا __interface وتسبقها __gc من اجل أن نجعلها نوع interface عادي .
بعدها قمنا بتعريف الكلاس Compute . أيضا هذا الكلاس مسبق ب __gc والتي تعني أن هذا الكلاس هو CTS Class وحياته سوف يتحكم بها ال CLR عوضا عن المستخدم . لاحظ العبارة التالية System::Math::Sqrt وفيها تم استخدم :: عوضا عن . لأن هذا هو الطبيعي في السي++ عند استخدام ال typename .
المثال ينتهي بالتابع الأساسي في سي++ وهو main . وتماما كما سبق فإن التابع main يقوم بحجز متحول من نوع Compute في ال heap والفرق الوحيد في تابع main هنا عن الكود السابق أنه قمنا باستخد __box وذلك لان التابع WriteLine يتوقع أن تكون بارمتراته من نوع الدليل أو reference وذلك يجب تغليف النوع value إلى reference باستخدام __box . طبعا هذا الأمر يتم في السي# والفجوال بيسك.نيت بصورة آلية . وهذا بسبب أن السي++ ليست مصممة أساسا لكي تدعم ال CLR .
الميزة التي قد نجدها في ال Managed C++ والغير موجودة في السي# أنه ممكن أن نعرف أنواع ولا نلحقها بال Garbage Collector مما يمنحنا فرصة للتحكم بعملية حذفها . هذا مهم إذا أردنا أنا نتحكم بالمؤشر بحرية . هذا الأمر ممكن بالسي# إذا استخدمنا الكلمة المحجوزة unsafe حتى نعرف منطقة غير managed ولكن بهذه الحالة يبقى جميع الكود غير منظم ويفقد الكثير من الخواص التي تتحلى بها سي# مثل السماحيات .
الميزات الأخرى في ال managed سي++ تتمثل وغير محصورة بالتالي :
امكانية استخدام using namespace لفتح المجالات مثلا الكود التالي يفتح المجال system فبالتالي ليس من الداعي ذكرها لاحقا :
using namespace System;
void main(void)
{
Console::WriteLine(“Hello”);
}وهي مشابهة تماما عندما نفتح المجال std في السي++ عندما نتعامل مع iostream .
الجدول التالي يوضح أنواع المتحولات في managed سي++ وربطها مع أنواع المتحولات الأساسية في دوت-نيت المتمثلة في CLR .
Byte unsigned char Char wchar_t Int16 short Int32 int, long Int64 __int64 UInt16 unsigned short UInt32 unsigned int, unsigned long UInt64 unsigned __int64 Single float Double double Decimal Decimal Boolean bool Structure struct String String* Class __gc class Interface __gc __interface Delegate __delegate
أحد الميزات التي تميز سي++ عن باقي لغات الدوت-نيت أن السي++ ممكنها أن تبني نوعين من البرامج : الأول يدعم ال .Net Framework وهي في managed c++ ، أما الآخر فهو مستقل ولا يحتاج إلى دوت-نيت وهي السي++ العادية .
طبعا كما في باقي لغات الدوت-نيت فإنه يتم ترجمت البرنامج إلى اللغة الوسيطية MSIL تماما كما هو الحال في managed c++ ولكن الأمر مختلف مع سي++ العادية حيث يتم من خلالها بناء تطبيق موجه مباشرة إلى معالج معين .