لرؤية الموضوع بشكل افضل :
http://www.flyarb.com/DotNet.aspx
لغة C# كما نعلم هي لغة برمجية تم تصميمها خصيصاً من اجل العمل تحت بيئة الدوت نت. في حقيقة الأمر هناك اكثر من 50 لغة برمجية موجودة من اجل كتابة التطبيقات لل.NET .. منها Haskell و Prolog و Ruby و Oberon و Delphi و Pascal و Pizza و COBOL و Smalltalk و Eiffel ....الخ
صممت مايكروسوفت ايضاً لغات خاصة بها للدوت نت مثل لغة C# و الفيجوال بيسك و C++/CLI و J# و Jscript.NET و F#.
مع كثرة هذه اللغات إلا أن لكل لغة محاسن ومساوئ . فبعض الأمور يمكن عملها بشكل اسهل مع لغة معينة لكن تكون صعبة ومعقدة في لغات اخرى ..
دائماً الclasses هي نفسها في اطار عمل الدوت نت لكن النمط الكتابي للغة قد يخرج لنا مميزات متنوعة من اطار العمل Framework.. فنحن امام كنز تتسابق لغات البرمجة لعرضه والاستفادة منه بالشكل الأفضل .. فمثلاً جملة using في C# تسهل استعمال تطبيق الobjects للانترفيس IDisposable , اما في لغات اخرى فتحتاج لكودات اكثر لعمل نفس الوظيفة.
فهذه المقالة ستعرض لك اهم الفوارق البرمجية بين لغات الدوت نت الأربع (C#, Visual basic , C++/CLI, F#) حيث ستشرح لك كيفية تحويل التطبيقات من لغة معينة إلى اخرى فاذا ما واجهت كود ما مكتوب بالفيجوال بيسك أو C++/CLI يمكنك بسهولة اعادة كتابته بالC# والعكس صحيح.. ايضاً سنتطرق إلى بعض المميزات البرمجية في كل لغة مع ذكر امثلة سهلة الفهم..
محتويات المقالة:
- Namespaces.
- تعريف الأنواع.
- Methods.
- Arrays.
- الجمل الشرطية.
- حلقات التكرار.
- معالجة الException.
- الوراثة.
- تحرير الموارد.
- Delegates.
- Events.
- Generics.
- استعلامات LINQ.
- دمج كودات الmanaged مع الكودات المحلية native (unmanaged) في C++/CLI.
في هذه المقالة افترضت أنك تعرف C# سابقاً .. لأن المقالة مخصصة حول اعطائك معلومات عامة عن كيفية تحويل افكار الC# التي تعرفها إلى اللغات الاخرى .. لذلك ليس من الضروري معرفة الفيجوال بيسك أو C++/CLI أو F# , وايضا لا تنتظر مني ان تتعلم تلك اللغات في هذه المقارنة البسيطة..
كما هو معروف فإن لغات الدوت نت الأكثر استعاملاً من مايكروسوفت هي C# و الفيجوال بيسك . فالاولى تم تصميمها حديثاً وتم وضع افكار كثيرة بها مقتبسة من C++ و Java و Pascal و لغات اخرى..
الفيجوال بيسك هي الإبن الروحي للVB6 حيث تم اضافة الكثير من مميزات البرمجة الكائنية إليه ليغدو في النهاية لغة جديدة تشبه في شكلها الVB6 لكنها ذات جوهر مختلف.
C++/CLI هي امتداد وتوسعة للغة C++ . الميزة الأضخم لها هو القدرة على خلط كودات native مع كودات managed , فيمكنك تطوير تطبيقات C++ المكتوبة بكودات native ودعمها بالوظائف التي تقدمها بيئة عمل الدوت نت . ويمكنك ايضاً اضافة الclasses التي للدوت نت إلى مكتبات native لذلك يمكن استعاملهم مع اللغات الاخرى مثل C#. ايضاً يمكنك كتابة تطبيقات كاملة مبنية على كودات managed باستعمال C++/CLI.
F# هي لغة جديدة تمت اضافتها مؤخراً بشكل رسمي في الفيجوال ستديو .. هذه اللغة تعتمد على البرمجة الوظيفية functional programming بشكل اساسي اضافة لدعمها للبرمجة كائنية التوجه التقليدية مما يجعلها تعمل جيدا مع الدوت نت بطبيعة الحال .. وحتى تحصل على بعض المعلومات حول معنى البرمجة الوظيفية في F# فالفنكشنات يمكن استعاملها كقيم ! وهذا يجعل من السهل انشاء واعدادا وتشكيل وتنظيم الوظائف من فنكشنات متعددة والقيام بمهام function pipelining حيث يتم ربط الوظائف (الفنشكنات) مع بعضها البعض ..
يمكن اعتبار F# اداة مثالية لطلاب الكليات العلمية وبالاخص في مجال الرياضيات .
من الأمثلة المشهورة على اللغات التي تستعمل مبدأ البرمجة الوظيفية لغة MATLAB..
NAMESPACES
تنظم كودات الدوت نت عادة داخل namespaces . يختلف النمط الكتابي لتعريف واستخدام الnamespaces بين اللغات الأربع بشكل قليل. ففي الC# يجب علينا استعمال الكلمة المحجوزة using اما C++/CLI تعتمد بشكل كامل على النمط الكتابي ل C++ عن طريق جملة using namespace . والفيجوال بيسك يفعل هذا الأمر عن طريق الكلمة المحجوزة Import بينما تعرف F# الكلمة المحجوزة open حتى تستعمل الnamespaces.
يمكن تعريف alias للclasses أو لnamespaces اخرى في C# والفيجوال بيسك . في حين مع C++/CLI يمكن عمل alias لnamespaces اخرى فقط اما عمل alias للclasses فهذا غير ممكن .
تحتاج C++ لاستعمال الكلمة المحجوزة namespace لتعريف الalias (نفس الكلمة المستخدمة لتعريف الnamespace) .. وكذلك تحتاج الفيجوال بيسك لاستعمال الكلمة المحجوز Import مرة اخرى.
حتى نعرف الnamespaces تستعمل جميع اللغات الأربع الكلمة المحجوزة namespace لكن يبقى هناك فرق.. ففي C++/CLI لا يمكن تعريف namespaces هرمية بجملة namespace واحدة بل يجب أن تكون الnamespaces متداخلة.
هناك ايضاً فرق مهم في اعدادات المشروع : حيث أن تعريف namespace في اعدادات المشروع في C# يعمل على تعريف namespace افتراضية تضاف في جميع الصفحات تلقائياً فوق الكودات لجميع العناصر الجديدية التي تضاف للمشروع . اما مع الفيجوال بيسك فيمكنك تعريف root namespace بحيث يستعمل من قبل كل العناصر الموجودة في المشروع . ولتعرف الNamespaces في السورس كود تقوم بتعريف sub-namespace داخل root namespace .
// C#using System;using System.Collections.Generic;using Assm = Najah.DifArticle.Assemblies;namespace Najah.DifArticle.Languages{}// C++/CLIusing namespace System;using namespace System::Collections::Generic;namespace Assm = Najah::DifArticle::Assemblies;namespace Najah{namespace DifArticle{namespace Languages{}}}' Visual BasicImports SystemImports System.Collections.GenericImports Assm = Najah.DifArticle.AssembliesNamespace Najah.DifArticle.LanguagesEnd Namespace// F#namespace Najah.DifArticle.Languagesopen Systemopen System.Collections.Generic تعريف الأنواع
تفرق الدوت نت بين انواع الValue وانواع الReference . فانواع الReference تعرف مع الclasses في C# بينما انواع الvalue تعرف مع structs . هذا القسم من المقالة سيعرض هذه الأمور اضافة إلى كيفية تعريف انترفيس (نوع رفرنس) و enumerations (نوع value).
- انواع Reference
تستعمل C# و الفيجوال بيسك الكلمة المحجوزة class لتعريف نوع reference ..بينما في C++/CLI الclasses والstruct نفس الشيئ تقريباً إلا انها لا تملك شيئ فاصل بين نوع reference ونوع value كما يوجد في C# و الفيجوال بيسك.
تمتلك لغة C++/CLI الكلمة المحجوزة ref لتعرف الmanaged class فيمكنك انشاء نوع reference عن طريق تعريف ref class و ref struct.
يحاط الكلاس في لغات C# و C++/CLI باقواس مجعدة , مع مراعاة وضع فاصلة منقوطة في نهاية تعريف الكلاس في C++/CLI اما الفيجوال بيسك يستعمل جملة End Class في نهاية الكلاس.
تنشأ F# الclasses وجميع الأنواع الاخرى عن طريق الكلمة المحجوزة type التي يتبعها اسم النوع اضافة للidentifier الاختياري as التي تعرف اسم الinstance identifier
// C#public class MyClass{}// C++/CLIpublic ref class MyClass{};public ref struct MyClass2{};' Visual BasicPublic Class MyClass2End Class// F# signature filetype MyClass() as this =// ... members of the class عندما تستعمل نوع reference فإن المتغير يحتاج إلى تعريف ويجب على الكائن أن يأخذ موقعاً على الmanaged heap.
تعرف C++/CLI عملية ^ (handler) عندما تعمل handle إلى نوع reference وهي مشابهة للC++ pointer *.
ايضاً يتوفر اداة gcnew التي تخصص ذاكرة معينة على الmanaged heap. . يمكنك مع C++/CLI تعريف متغير محلي ايضاً لكن لانواع reference يبقى الكائن موجود على الmanaged heap.
تعريف المتغير في الفيجوال بيسك يبدأ بكلمة Dim متبوعة باسم المتغير ومع new ونوع الكائن يتم تخصيصه على الmanaged heap.
// C#MyClass obj = new MyClass();var obj2 = new MyClass()// C++/CLIMyClass^ obj = gcnew MyClass();MyClass obj2;' Visual BasicDim obj as New MyClass2() اذا كان نوع reference لا يؤشر على ذاكرة فإنه يتم استعمال كلمات محجوزة مختلفة .. فالC# تعرف حرف null و C++/CLI تعرف nullptr (مع العلم أن NULL تعمل فقط مع انواع native) اما الفيجوال بيسك تعرف كلمة Nothing .
قيمة null لا تستعمل عادة في F# فهي قيمة غير مسموح بها مع الأنواع التي يتم تعريفها في تلك اللغة . لكنك باستعمال انواع معرفة بلغات غير F# يمكنك الحصول على قيم null.
لاحظ الجدول التالي الذي يعرض انواع reference المعرفة . لا تعرف C++/CLI نوع string أو object مثل ما يتم في اللغات الاخرى . بطبيعة الحال يمكن استعمال الclasses المعرفة عن طريق اطار العمل
نوع .NET C# C++/CLI فيجوال بيسك F#
System.Object
object غير معرف Object obj
System.String string غير معرف String string
- انواع Value
لتعريف انواع Value . تستعمل لغة C# الكلمة المحجوزة struct بينما تستعمل C++/CLI الكلمة المحجوزة value اما الفيجوال بيسك فيتم استعمال Structure .والF# تستعمل الكلمة المحجوزة type مع [<Struct>] attribute ويمكن استعمال الكلمات المحجوزة struct و end.
// C#public struct MyStruct{}// C++/CLIpublic value class MyStruct{};' Visual BasicPublic Structure MyStructEnd Structure// F#[<Struct>]type MyStruct =val x : inttype MyStruct2 =structval x : intend يمكن مع C++/CLI تخصيص نوع value على الstack وعلى native heap باستعمال اداة new , اما على managed heap يكون باستعمال اداة gcnew.
لا تمتلك C# واليجوال بيسك هذه الخيارات لكن هذه الأدوات تصبح مهمة عند خلط كودات الmanaged والnative مع C++/CLI.
// C#MyStruct test;// C++/CLIMyStruct test1;MyStruct* ptest2 = new MyStruct();MyStruct^ htest3 = gcnew MyStruct();' Visual BasicDim test as MyStruct لاحظ انواع value المعرفة في الجدول التالي : نوع char في C++/CLI حجمه فقط 1 byte لحرف ASCII بينما في C# حجمه 2 bytes لاحرف Unicode مثل wchar_t في C++/CLI.
معيار ANSI للC++ يعرف فقط short <= int <= long . مع اجهزة 32 بت تمتلك int و long حجم 32 بت فاذا اردت تعريف متغير 64 بت في C++ يجب عليك استعمال long long.
نوع .NET C# C++/CLI فيجوال بيسك F# الحجم
Char char wchar_t Char char 2 bytes
Boolean bool bool Boolean bool 1 byte يحتوي true او false
Int16 short short Short int16 2 bytes
UInt16 ushort unsigned short UShort uint16 2 bytes بلا اشارة
Int32 int int Integer int 4 bytes
UInt32 uint unsigned int UInteger uint 4 bytes بلا اشارة
Int64 long long long Long int64 8 bytes
UInt64 ulong unsigned long long ULong uint64 8 bytes بلا اشارة
استدلال النوع
تسمح لك C# بتعريف متغير محلي من دون التصريح بنوعه مع الكلمة المحجوزة var . ويتم الإستدلال على نوع هذا المتغير من قبل الكومبايلر عن طريق القيمة الأولية التي يهيئ فيها هذا المتغير.
تقدم الفيجوال بيسك نفس الميزة عن طريق الكلمة المحجوزة Dim طالما انت مفعل خيار option infer وهذا يمكن أن يتم مع وضع خيار infer+ للكومبايلر في سطر الترجمة أو بتفعيله من صفحة اعدادات المشروع.
يتم الإستدلال بانواع المتغيرات والقيم والباراميترز في F# عن طريق الكلمة المحجوزة let.
// C#var x = 7;' Visual BasicDim x = 7// F#let x = 7 Interfaces
تعريف الinterfaces متشابه جداً للثلاث لغات التي تستعمل الكلمة المحجوزة interface لكنها تختلف مع F# لأن الinterfaces تعرف مع type يحتوي فقط اعضاء من نوع abstract.
// C#public interface IDisplay{void Display();}// C++/CLIpublic interface class IDisplay{void Display();};' Visual BasicPublic Interface IDisplaySub DisplayEnd Interface// F#[<Interface>]type public IDisplayabstract member Display : unit -> unit التصريح عن الinterfaces يختلف بين اللغات ففي C# و C++/CLI يتم وضع فاصلة بعد اسم الكلاس تتبع باسم الإنترفيس ثم يتم التصريح عن الmethods المعرفة مع الإنترفيس .. في C++/CLI يجب أن تصرح عن الmethods بvirtual اما الفيجوال بيسك فتستعمل الكلمة المحجوزة Implements حتى تصرح عن الإنترفيس اضافة إلى أن الmethods التي تعرف بالإنترفيس تحتاج ايضا لالحاق الكلمة المحجوزة Implements بها.
تضع F# الinterfaces المعرفة في القسم ضمن تعريف النوع . فيتم التصريح عن الإنترفيس بالكلمة المحجوزة interface و with ومن ثم التصريح عن كل الmethods للانترفيس التالي:
// C#public class Student: IDisplay{public void Display(){}}// C# explicit interface implementationpublic class Student: IDisplay{void IDisplay.Display(){}}// C++/CLIpublic ref class Student: IDisplay{public:virtual void Display() { }};' Visual BasicPublic Class StudentImplements IDisplayPublic Sub Display Implements IDisplay.DisplayEnd SubEnd Class Enumerations
تعرف الEnumeration بشكل متشابه في الثلاث لغات مع الكلمة المحجوزة enum ( الفيجوال بيسك فقط تستعمل سطر جديد بدلا من الفاصلة من اجل فصل العناصر).
تستعمل F# الكلمة المحجوزة type و تضيف(|) قبل بداية كتابة أي عنصر.
// C#public enum Color{Red, Green, Blue}// C++/CLIpublic enum class Color{Red, Green, Blue};' Visual BasicPublic Enum ColorRedGreenBlueEnd Enum// F#type Color =| Red = 0| Green = 1| Blue = 2 METHODS
باستثناء F# .. دائماً تكتب الmethods وتعرف داخل كلاس . النمط الكتابي لC++/cLI مشابه ل C# باستثناء أن معيار الوصول access modifier ليس جزءاً من جملة التصريح عن الميثود حيث أنه يتم كتبايته قبل ذلك (معيار الوصول access modifier يجب أن ينتهي بفاصلة ).
في الفيجوال بيسك يتم استعمال الكلمات المحجوزة Sub أو Function للتعريف الميثود . اما مع F# فيمكن تعريف الفنكشن بالكلمة المحجوزة let و test كباراميتر و y و ترجع y+8.
يمكن التصريح عن انواع الباراميتر والنوع المرجع return type بفنكشن كما هو مبين. فمع فنكشن add مثلاً x و y من نوع int ويرجع نوع int . اذا لم يتم التصريح عن تلك الأنواع يتم الإستدلال عنها من قبل الكومبايلر . مع فنكشن test تكون y هي int لأن 8 جمعت إلى y و8 هي int بطبيعة الحال.
اما بخصوص تعريف الميثود ضمن كلاس فهذا يتم عبر الكلمة المحجوزة member .
تمتلك الInstance method بادئة في تعريف الكلاس وهي as. لا تستعمل F# كلمات this أو Me أو self للوصول للinstance الحالي لكن يمكنك تعريف أي identifier تريد .
// C#public class MyClass{public void test(){}}// C++/CLIpublic ref class MyClass{public:void test(){}};' Visual BasicPublic Class MyClass1Public Sub testEnd SubEnd Class// F# functionlet test y = y + 8let add (x : int, y : int) : int = x + y// method within a classtype MyClass as this =member this.test() =printfn "MyClass.test" Methods Parameters و Return Types
تعرف الباراميترز الممرة لل methods في C# و C++/CLI داخل اقواس. يتم وضع نوع الباراميتر قبل اسم المتغير . ايضاً اذا كانت تلك الميثود ترجع قيمة يجب تعريف تلك الميثود بنوع هذه القيمة التي ترجعها بدلاً من void .
تستعمل لغة الفيجوال بيسك كلمة Sub للتصريح عن ميثود لا ترجع أي قيمة وتستعمل كلمة Function مع الميثود التي ترجع نوع معين. ثم بعد وضع احدى هذه الكلمات يتم وضع النوع المرجع (في حالة استعملنا كلمة Function) ثم اسم الميثود والأقواس. تمتلك لغة الفيجوال بيسك ترتيب مختلف عند التصريح عن المتغير ونوع الباراميتر حيثأن اسم المتغير يسبث نوعه بعكس C# و C++/CLI.
اما F# اذا كانت الميثود لا ترجع أي قيمة يتم التصريح عنها ب unit (unit هي مشابهة لC# void) . كما ترون في المثال التاي تم تعريف الميثود test لاستقبال باراميتر int ولارجعا قيمة int .
// C#public class MyClass{public int test(int i){return 2 * i;}}// C++/CLIpublic ref class MyClass{public:int test(int i){return 2 * i;}};' Visual BasicPublic Class MyClass2Public Sub test1(ByVal i as Integer)End SubPublic Function test(ByVal i As Integer) As IntegerReturn 2 * iEnd SubEnd Class// F#type MyClass as this =member this.test(i : int) : int = i * 2 Parameter Modifier
تمرر انواع value بالقيم بينما انواع reference تمرر بالرفرنس بشكل تلقائي. فاذا كان نوع value الممرر كباراميتر يجب أن يتغير خلال استدعاء الميثود تستطيع استعمال parameter modifier مع C# وهو ref.
تعرف C++/CLI اداة managed للرفرنس وهي % , هذه الأداة مشابهة للموجودة في C++والتي هي & باستثناء أن % يمكن استعمالها مع انواع الmanaged .
يمكن للgarbage collector أن يتابع هذه الكائنات في حالة انهم تم تغيير موقعهم ضمن الmanaged heap .
اما في لغة الفيجوال بيسك فيتم استعمال الكلمة المحجوزة ByRef من اجل تمرير باراميترز من نوع reference.
// C#public class testParameterPassing{public void testChangeVal(ref int i){i = 7;}}// C++/CLIpublic ref class testParameterPassing{public:int testChangeVal(int% i){i = 7;}};' Visual BasicPublic Class testParameterPassingPublic Sub testChangeVal(ByRef i as Integer)i = 7End SubEnd Class عند اعطاء ميثود باراميترز من نوع reference .. فقط في لغة C# تحتاج لتطبيق parameter modifier . اما C++/CLI و الفيجوال بيسك لا تفرقان بين استدعاء ميثود مزود ب parameter modifier أو لا . هنا تمتلك C# ميزة مهمة لإنك تستطيع فوراً رؤية ان الباراميتر يمكن أن تتغير في الميثود المستدعاة.
الفيجوال بيسك لا تسمح لك بعمل overload methods فقط بتغيير الmodifier وذلك بسبب النمط الكتابي للمستدعي الذي لا يميز بينهم. اما كومبايلر C++/CLI فيسمح لك بعمل overload للميثود فقط بتغيير الmodifier لكن لا يمكن لك ترجمة كود المستدعي لأان الطريقة المعمول بها تبقى غامضة وغير واضحة .
يمكن عمل overload مع C# واستعمال الmethods فقط بتغيير parameter modifier لكن هذا ليس وسيلة جيدة للبرمجة ولا ينصح فيه.
// C#testParameterPassing obj = new testParameterPassing();int a = 1;obj.testChangeVal(ref a);Console.WriteLine(a); // writes 7// C++/CLItestParameterPassing obj;int a = 1;obj.testChangeVal(a);Console.WriteLine(a); // writes 7' Visual BasicDim obj as new testParameterPassing()Dim i as Integer = 1obj.testChangeVal(i)Console.WriteLine(i) // writes 7 ملاحظة : تعرف ايضا لغة C# الكلمة المحجوزة out عندما يتم ارجاع الباراميتر لتوه من الميثود. هذا الأمر غير موجود في الفيجوال بيسك وC++/CLI طالما أن المستدعي والمستدعى في نفس الapplication domain . في الواقع لا يوجد فرق حقيقي بين out و ref خلف الكواليس ويمكنك استعمال ميثود معرفة بباراميتر out الخاص بC# من لغات فيجوال بيسك و C++/CLI بنفس طريقة استعمال ref لذلك يمكنك استعمال الattribute [out] مع الفيجوال بيسك و C++/CLI في حال استعملت ميثود عير الapplication domains او عبر processes.
Constructors
يكون للconstructor نفس اسم الكلاس في C# و C++/CLI بينما في الفيجوال بيسك يتم استعمال procedure اسمه New.
تستعمل الكلمات المحجوزة this و Me للوصول إلى هذا الinstance عندما نستحضر constructor اخر ضمن الconstructor نفسه .
من المطلوب تهيئة الmember في C# . ايضاً من الممكن وضع constructor اخر كميثود في C++/CLI والفيجوال بيسك.
تظهر F# اختلافاً قليلاً حيث يتم تعريف constructor واحد مع type . يأخذ الconstructor مع Student اثنان من parameters من نوع الstring . يتم تعريف الconstructors الاخرين عن طريق الكلمة المحجوزة new.
كما نلاحظ في المثال فnew() تعرف constructor بدون باراميترز والconstructor الموضوع مع كلاس student موجود فيه اثنين من الباراميترز.
// C#public class Student{public Student(): this("unknown", "unknown") { }public Student(string firstName, string lastName){this.firstName = firstName;this.lastName = lastName;}private string firstName;private string lastName;}// C++/CLIpublic ref class Student{public:Student(){Student("unknown", "unknown");}Student(String^ firstName, String^ lastName){this->firstName = firstName;this->lastName = lastName;}private:String^ firstName;String^ lastName;};' Visual BasicPublic Class StudentPublic Sub New()Me.New("unknown", "unknown")End SubPublic Sub New(ByVal firstName As String, ByVal lastName As String)Me.MyFirstName = firstNameMe.MyLastName = lastNameEnd SubPrivate MyFirstName As StringPrivate MyLastName As StringEnd Class// F#type Student(firstName0 : string, lastName0 : string) as this =let mutable firstName, lastName = firstName0, lastName0new () = Student("unknown", "unknown") Properties
حتى نعرف property نحتاج في C# إلى اثنين من الaccessors وهما set و get ضمن جسم الproperty.
تعرف C# و C++/CLI و الفيجوال بيسك طريقة اختزالية مختصرة لتعريف الproperty حيث إننا لسنا بحاجة لوضع التعريف الكامل في حال اردنا فقط ارجاع قيمة او وضعها عن طريق get و set. فهذه طريقة تلقائية يعمل الكومبايلر على تمديدها لديه مما يوفر عليك الوقت والجهد .
تحتاج C++/CLI لوضع نوع مرجع مع get accessor وتحتاج لنوع الباراميتر مع set accessor .
بخصوص النسخة القصيرة لكتابة property مع C++/CLI يجب عليك تعريف النوع والإسم للproperty . فيتم انشاء get و set تلقائياً من قبل الكومبايلر . فاذا كان لا يوجد مطلوب سوى وضع وارجاع قيم فالنسخة المختصرة تكفي بشكل جيد . اماذا اذا كان تعريف get accessor يحتاج اكثر من ذلك ( مثلاً فحص قيمة أو عمل refresh ) يجب عليك كتابة النمط الكامل لكود الProperty.
تعرف F# الproperties ك members للكلاس باستعمال with get() لل get accessor و and set(value) للset accessor.
// C#public class Student{private string firstName;public string FirstName{get { return firstName; }set { firstName = value; }}public string LastName { get; set; }}// C++/CLIpublic ref class Student{private:String^ firstName;public:property String^ FirstName{String^ get(){return firstName;}void set(String^ value){firstName = value;}}property String^ LastName;};' Visual BasicPublic Class StudentPrivate myFirstname As StringPublic Property FirstName()GetReturn myFirstNameEnd GetSet(ByVal value)myFirstName = valueEnd SetEnd PropertyPublic Property LastName As StringEnd Class// F#type Student(firstName0 : string, lastName0 : string) as this =let mutable firstName, lastName = firstName0, lastName0member this.FirstNamewith get() = firstNameand set(value) = firstName <- valuemember this.LastNamewith get() = lastNameand set(value) = lastName <- value تمتلك read-only properties مع C# و C++/CLI فقط get accessor. اما مع الفيجوال بيسك يجب عليك تحديد ReadOnly modifier . اما writeOnly properties يجب أن تعرف ب WriteOnly modifiers و set accessor.
' Visual BasicPublic ReadOnly Property Name()GetReturn myFirstName & " " & myLastNameEnd GetEnd Property - تهيئة الobjects
يمكنك أن تهيئ الproperties باستعمال object initialize في C# و الفيجوال بيسك وذلك باستعمال اقواس مجعدة بشكل مباشر لتهيئة الarray. النمط الكتابي لذلك في C# والفيجوال بيسك متشابه جدا فقط انك تستعمل في الفيجوال بيسك كلمة with المحجوزة.
// C#Student p = new Student { FirstName = "Abed", LastName = "Bukhari" };' Visual BasicDim p As New Student With { .FirstName = "Abed", .LastName = "Bukhari" } Extension Methods
ال Extension Methods هم اساس الLINQ. ففي كل من الC# والفيجوال بيسك يمكنك انشائها .. والنمط الكتابي يختلف حيث تؤشر C# على ال Extension Method بالكلمة المحجوزة this في الباراميتر الأول , اما الفيجوال بيسك يؤشر على ال Extension Method ب attribute <Extensions> بينما في F# يكون التعبير لل Extension Methods هو type extension . يتم عمل type extension بتحديد الإسم الكامل fully qualified name للنوع مع التصريح ب type ونضيف عليها الكلمة المحجوزة with.
// C#public static class testStringExtension{public static void test(this string s){Console.WriteLine(""test {0}", s);}}' Visual BasicPublic Module testStringExtension<Extension()> _Public Sub test(ByVal s As String)Console.WriteLine(""test {0}", s)End SubEnd Module// F#module Najah.DifArticle.Languages.testStringExtensiontype System.String withmember this.test = printfn "String.test" Static Members
الstatic field يتم على instance مؤة واحدة فقط لجميع الكائنات من النوع . تستعمل C# و C++/CLI الكلمة المحجوزة static بينما تقدم لغة الفيجوال بيسك الوظيفة نفسها عن طريق الكلمة المحجوزة Shared.
ولكي تستعمل static members يمكنك وضع اسم الكلاس متبوع ب . ثم اسم ذلك الstatic member.
ملاحظة : تستعمل C++/CLI الرمز : للوصول إلى الstatic members.
// C#public class testSingleton{private static SomeData data = null;public static SomeData GetData(){if (data == null){data = new SomeData();}return data;}}// use:SomeData d = testSingleton.GetData();// C++/CLIpublic ref class testSingleton{private:static SomeData^ hData;public:static SomeData^ GetData(){if (hData == nullptr){hData = gcnew SomeData();}return hData;}};// use:SomeData^ d = testSingleton::GetData();' Visual BasicPublic Class testSingletonPrivate Shared data As SomeDataPublic Shared Function GetData() As SomeDataIf data is Nothing Thendata = new SomeData()End IfReturn dataEnd FunctionEnd Class' Use:Dim d as SomeData = testSingleton.GetData() ARRAYS
دائماً كلاس Array في الدوت نت يكون اساس جميع العمليات على الarrays. عندما نعرف array ينشأ الكومبايلر كلاس مشتق من الكلاس الأساسي Array. فعندما تم تصميم C# وضع المصممون لها النمط الكتابي للاقواس للarrays من C++ ووسعوها ب Array initializer .
// C#int[] myarr1 = new int[3] {1, 2, 3};int[] myarr2 = {1, 2, 3}; اذا استعمل الأقواس مع C++/CLI سوف تنشأ C++ Array من نوع native لكنها ليست array مبنية كلاس Array . حتى تنشأ .NET array تعرف C++/CLI الكلمة المحجوزة array . هذه الكلمة تستعمل شكل شبه عام مع اقواس محدبة على شكل زاوية يتم تعريف نوع العناصر ضمنها . تدعم C++/CLI تهيئة العناصر بشكل مشابه للC#.
// C++/CLIarray<int>^ myarr1 = gcnew array<int>(3) {1, 2, 3};array<int>^ myarr2 = {1, 2, 3}; تستعمل لغة الفيجوال بيسك الأقواس للarrays مع العلم انها تحتاج رقم العنصر الاخير بدلا من عدد العناص مع تعريف الarray.
كل لغات .NET تبدأ الarrays بالعنصر رقم 0 وهذا يشمل ايضا الفيجوال بيسك .. وحتى نجعل ذلك واضحها قدمت لغة الفيجوال بيسك 2010 تعبير 0 To number مع تعريف الarrays . فدائما يتم البدء ب 0 .. 0 To فقط لجعل هذا اكثر قابلية للقراءة . تدعم لغة فيجوال بيسك ايضا تهيئة الarray اذا كانت تلك الarray مهيئة باداة New
' Visual BasicDim myarr1(0 To 2) As Integer()Dim myarr2 As Integer() = New Integer(0 To 2) {1, 2, 3}; تقدم لغة F# طرق مختلفة لتهيئة الarray. حيث أن الarrays ذات الأجحام الصغيرة يمكن انشائهم باستعمل [| و |] كاقواس فتح واغلاق ويتم فصل العناصر بالفاصلة المنقوطة.
التعبير المتسلسل مثل جملة for in يمكن استعمالها لتهيئة العناصر في الarray كما قمنا مع myarr2 . اما myarr3 فقد تم تهيئتها بنوع zeroCreate امتدادا إلى كلاس Array من اجل انشاء وتهيئة 30 عنصر.
يمكن الوصول للArrays عن طريق الindexer . من الممكن الوصول لنطاق معين من الarray عن طريق slices فمثلاً myarr2.[6..] تصل لجميع العناصر ابتداءً من العنصر السابع حتى النهاية.
// F#let myarr1 = [| 1; 2; 3 |]let myarr2 = [| for i in 1..10 -> i |]let myarr3 : int aray = Array.zeroCreate 20printfn "%d" myarr1.[0]// array slicemyarr2.[6..] الجمل الشرطية
تحدد الجمل الشرطية أي الكودات التي يجب أن تعمل . فكلما زاد عددها في برامجك كلما زاد ذكاءها .
تعرف C# جمل if و switch اضافة لتعبير الأداة الشرطي (؟) .
- جملة if
جملة if في C# هي مثل C++/CLI . اما لغة الفيجوال بيسك فتستعمل If-Then/Else/End If بدلاً من الأقواس المجعدة
// C# and C++/CLIif (a == 3){// do this}else{// do that}' Visual BasicIf a = 3 Then' do thisElse' do thatEnd If - الأداة الشرطية (؟)
تدعم لغة C++/CLI الأداة الشرطية (؟) وهي نسخة مخففة من جملة if . تعرف هذه الاداة في C++/CLI باسم اداة ternary.
يحتوي الargument الأول على جملة منطقية فاذا كانت نتيجتها true يتم تنفيذ المقدار الاول , عدا ذلك يتم تنفيذ المقدار الثاني.
تمتلك لغة الفيجوال بيسك فنكشن IIf ضمنمكتبة الRuntime الخاصة به والذي يقدم نفس الوظيفة.
اما مع F# فif/then/else تستعمل كأداة شرطية
// C#string s = a > 3 ? "one": "two";// C++/CLIString^ s = a > 3 ? "one": "two";' Visual BasicDim s As String = IIf(a > 3, "one", "two")// F#if a = 3 then printfn "do this" this else printfn "do that" - جملة switch
تبدو جملة switch متشابهة جداً في C# و C++/CLI لكن هناك فروقات مهمة.
تدعم C# الstrings في اختيار الCase وهذا غير ممكن في C++ حيث انه يجب عليك استعمال if-else بدلاً من ذلك.
تدعم C++/CLI الإنحدار الضمني من case إلى التي بعديها (عندما لا يكون هناك break) بينما في C# الكومبايلر سينشئ خطأ اذا لم يجد جمل break أو goto.
هناك حالة واحدة في C# مسموحة للإنحدار الضمني من case إلى التي بعدها وهي فقط في حالة ان لا يكون هناك اي جملة داخل الcase.
تمتلك لغة الفيجوال بيسك جملة Select/Case بدلا من switch/case . كلمة break ليست فقط غير محتاجة بل إنها غير ممكنة اصلاً. اما الإنحدار الضمني من case إلى التي بعدها غير ممكن حتى لو ابقيت الcase فارغة من الجمل . فبدلاً من ذلك يمكن للCase أن تجمع مع And و Or و To (مثلا 5 To 9) .
تمتلك F# تعابير match مع الكلمات المحجوزة match و with . كل سطر للmatches يبدأ ب | متبوع بعبارة.
المثال يستعمل عبارة رمز OR مع Tshirt.Abed | Tshirt.Ahmad . اما العبارة الإخيرة فهي افتراضية بديلة.
// C#string GetColor(Tshirt s){string color;switch (s){case Tshirt.Abed :case Tshirt.Ahmad:color = "Red";break;case Tshirt.Ezz:case Tshirt.Rami:color = "Black";break;default:color = "Unknown";break;}return color;}// C++/CLIString^ GetColor(Tshirt s){String^ color;switch (s){case Tshirt::Abed :case Tshirt::Ahmad:color = "Red";break;case Tshirt::Ezz:case Tshirt::Rami:color = "Black";break;default:color = "Unknown";break;}return color;}' Visual BasicFunction GetColor(ByVal s As Tshirt) As StringDim color As String = NothingSelect Case sCase Tshirt.Abed And Tshirt.Ahmadcolor = "Red"Case Tshirt.Ezz And Tshirt.Ramicolor = "Black"Case Elsecolor = "Unknown"End SelectReturn colorEnd Function// F#type TshirtTest =static member GetColor(s : Tshirt) : string =match s with| Tshirt.Abed | Tshirt.Ahmad -> "Red"| Tshirt.Ezz | Tshirt.Rami -> "Black"| _ -> "Unknown" حلقات التكرار
يتم تنفيذ الكود مع حلقات التكرار عدة مرات حتى يتحقق الشرط. حلقات التكرار المعرفة هي (for و while و do..while و foreach)
لغتا C# و C++/CLI متشابهتان جداً بطريقة كتابة هذه الجمل اما لغات F# والفيجوال بيسك تقدمان طريقة مختلفة.
- حلقة for
حلقة for متشابهة في C# و C++/CLI . اما مع الفيجوال بيسك فلا يمكنك تهيئة متغير داخل جملة for/to فيجب عليك تهيئة المتغير مسبقاً. حلقة for/to لا تحتاج عادة لالحاق الكلمة المحجوزة step بها (حيث انها في الحالية الافتراضي تكون الstep هي 1) فقط في حالة انك لا تريد الزيادة بمقدار 1 تكون كلمة step مطلوبة للعمل مع for/to .
لغة F# تستعمل الكلمات المحجوزة for/to/do و for/downto/do.
// C#for (int i = 0; i < 100; i++){Console.WriteLine(i);}// C++/CLIfor (int i = 0; i < 100; i++){Console::WriteLine(i);}' Visual BasicDim count as IntegerFor count = 0 To 99 Step 1Console.WriteLine(count)Next// F#for i = 1 to 10 doprintfn ifor i = 10 downto 1 doprintfn i - حلقات while و do..while
حلقات while و do..while متشابهة في C++/CLI و C .تمتلك لغة الفيجوال بيسك تركيب مشابه جداً مع Do While/Loop و Do/Loop While.
اما F# لا تمتلك do/while لكن يوجد لديها while/do.
// C#int i = 0;while (i < 3){Console.WriteLine(i++);}i = 0;do{Console.WriteLine(i++);} while (i < 3);// C++/CLIint i = 0;while (i < 3){Console::WriteLine(i++);}i = 0;do{Console::WriteLine(i++);} while (i < 3);' Visual BasicDim num as Integer = 0Do While (num < 3)Console.WriteLine(num)num += 1Loopnum = 0DoConsole.WriteLine(num)num += 1Loop While (num < 3)// F#let i = 0let mutable looping = truewhile looping doprintfn "%d" ii++if i > = 3looping < - false - جملة foreach
تصنع جملة foreach استعمال للنترفيس IEnumerable . جملة foreach غير موجودة في ANSI C++ لكن تمت اضافتها في C++/CLI . هناك فراغ بين for و each في C++/CLI على الإختلاف مع C#.
تستعمل F# هذه الوظيفة للكلمات المحجوزة for/in/do.
int[] arr = {1, 2, 3};foreach (int i in arr){Console.WriteLine(i);}// C++/CLIarray < int > ^ arr = {1, 2, 3};for each (int i in arr){Console::WriteLine(i);}' Visual BasicDim arr() As Integer = New Integer() {1, 2, 3}Dim num As IntegerFor Each num as Integer In arrConsole.WriteLine(num)Next// F#for i in arr doprintfn i ملاحظة: تجعل foreach التكرار عبر الcollection سهل للغاية . ايضاً تدعم C# انشاء enumerations عبر استخدام الكلمة المحجوزة yield لكن مع الفيجوال بيسك و C++/CLI هذه الكلمة غير متوفرة . لكن بدلاً من ذلك تعمل implement للانترفيس IEnumerable الذي يقوم يدوياً بالعمل مع هذه اللغات كجملة yield .
معالجة الEXCEPTION
معالجة الException متشابهة للغاية بين الثلاث لغات . حيث أن جميع هذه اللغات تستعمل try/catch/finally من اجل معالجة الexceptions. وتستعمل الكلمة المحجوزة throw من اجل انشاء exception.
تستعمل F# كلمة raise من اجل انشاء exceptions و try/with/finaly من اجل معالجة الexceptions
// C#public void Method(Object o){if (o == null)throw new ArgumentException("Error");}public void test(){try{Method(null);}catch (ArgumentException ex){ }catch (Exception ex){ }finally{ }} // C++/CLIpublic:void Method(Object^ o){if (o == nullptr)throw gcnew ArgumentException("Error");}void test(){try{Method(nullptr);}catch (ArgumentException^ ex){ }catch (Exception^ ex){ }finally{ }} ' Visual BasicPublic Sub Method(ByVal o As Object)If o = Nothing ThenThrow New ArgumentException("Error")End SubPublic Sub test()TryMethod(Nothing)Catch ex As ArgumentException'Catch ex As Exception'Finally'End TryEnd Sub // F#type ExceptionTest() as this =member this.Method(o : obj) =if o = null thenraise(ArgumentException("error"))member this.test=trytrythis.Method(null)with| :? ArgumentException as ex -> printfn "%s" ex.Message| :? IOException as ex -> printfn "%s" ex.Messagefinallyprintfn "finally" الوراثة
تقدم لغات الدوت نت عدة كلمات محجوزة لتعريف Polymorphism ولعمل override أو اخفاء للmethods وتقدم معايير وصول access modifier للسامح او عدم السماح للوصول للmember.
الوظيفة متشابهة جداً في C# و C++/CLI و الفيجوال بيسك لكن الكلمات المحجوزة تختلف.
- معايير الوصول Access Modifiers
معايير الوصول للC++/CLI والفيجوال بيسك وF# مشابهة جداً للC# لكن هناك بعض الإختلافات الملحوظة. حيث ان لغة الفيجوال بيسك تستعمل معيار وصول Friend بدلاً من internal للوصول إلى الأنواع في نفس الأسمبلي.
C++/CLI لديها معيار وصول حصري وهو protected private .
معيار internal protected يسمح للوصول للmembers التي ضمن نفس الأسمبلي وايضاً من اسمبلي اخر لكن بشرط أن يكون النوع مشتق من الbase type.
لغتا C# والفيجوال بيسك لا يمتلكون طريقة للسماح للانواع المشتقة فقط ضمن نفس الأسمبلي . لكن هذا ممكن مع protected private في C++/CLI . فهنا الprivate تعني خارج الأسمبلي لا يمكن الوصول لكن مسموح داخل الأسمبلي بشرط ان يكون نوع الوصول protected. الترتيب (سواء كتبت protected private أو private protected) فهو غير مهم.
ملاحظة : معدل وصول السماح يقع دائماً اكثر داخل الأسمبلي بينما يكون اقل خارج الأسمبلي.
تستعمل F# ثلاثة معايير وصول لكن ايضا تسمح ب Protected اذا كان النوع يجب أن يستعمل من لغات دوت نت اخرى.
معايير الوصول access modifier يمكن أن توضع في F# ضمن ملفات signature.
الجدول التالي يعرض معايير الوصول لكل من اللغات.
C# C++/CLI فيجوال بيسك F#
public Public Public public
protected Protected Protected
private Private Private private
internal Internal Friend internal
internal protected internal protected Protected Friend
غير ممكن protected private غير ممكن
ملفات الsignature للF# تكون بامتداد .fsi ويتم فيها تعريف الsignature لل members.
مع ملاحظة انه يمكن انشائهم بشكل تلقائي مع خيار الكومبايلر –sig .. وحسب متطلبات العمل فقط معايير الوصول هي التي تحتاج للتغير.
// F#type public Student =classinterface IDisplaypublic new : unit -> Studentpublic new : firstName:string * lastName:string -> Studentoverride ToString : unit -> stringmember public FirstName : stringmember public LastName : stringmember public FirstName : string with setmember public LastName : string with setend - الكلمات المحجوزة
الكلمات المحجوزة المهمة للوراثة تم وضعها في الجدول التالي
C# C++/CLI فيجوال بيسك F# الوظيفة
: : Implements interface with عمل implement للانترفيس
: : Inherits inherit تعمل inherit من base class
virtual virtual Overridable abstract تعرف ميثود تدعم Polymorphism
overrides override Overrides override تعمل override لميثود virtual
new new Shadows تعمل hide للميثود من base classe
abstract abstract MustInherit [<AbstractClass] Abstract class
sealed sealed NotInheritable [<Sealed>] Sealed class
abstract abstract MustOverride abstract Abstract method
sealed sealed NotOverridable Sealed method
this this Me يؤشر على الكائن الحالي
base Classname:: MyBase base يؤشر على الbase class
ترتيب وضع الكلمات المحجوزة مهم في اللغات .
في المثال التالي: الabstract base class الذي هو BaseSample والذي يحتوي على ميثود واحدة معرفة من نوع abstract وميثود واحدة معرفة ب virtual .
الكلاس BaseSample تم اشتقاقه من قبل الكلاس DerivedSample في حين تم تعريف الميثود التي من نوع abstract وتم عمل override للميثود التي من نوع virtual .
// C#public abstract class BaseSample{public virtual void test1(){}public abstract void test2();}public class DerivedSample: BaseSample{public override void test1(){base.test1();}public override void test2(){}} // C++/CLIpublic ref class BaseSample abstract{public:virtual void test1(){}virtual void test2() abstract;};public ref class DerivedSample: public BaseSample{public:virtual void test1() override{BaseSample::test1();}virtual void test2() override{}}; ' Visual BasicPublic MustInherit Class BaseSamplePublic Overridable Sub test1()End SubPublic MustOverride Sub test2()End ClassPublic class DerivedSampleInherits BaseSamplePublic Overrides Sub test1()MyBase.test1()End SubPublic Overrides Sub test2()End SubEnd Class // F#[<AbstractClass>]type BaseSample () as this =abstract test2 : unit -> unitdefault this.test1() = printfn " BaseSample.test1"abstract test1 : unit -> unittype DerivedSample() as this =inherit BaseSample ()override this.test1() =base.test2()printfn " DerivedSample.test1"override this.test2() = printfn " DerivedSample.test2" ادارة الموارد RESOURCE MANAGEMENT
سندرس في هذا القسم كيفة عمل implementing للانترفيس IDisposable وعمل implementing ل finalizer.
عمل implementing للانترفيس IDisposable
لتحرير الموارد يعرف الإنترفيس IDisposable مثيود Dispose() . يجب عليك عمل implement للانترفيس IDisposable في لغات C# و الفيجوال بيسك و F# . لكن مع C++/CLI الإنترفيس IDisposable معمول له implement لكن هذا يتم فقط عن طريق الكومبايلر اذا كتبت فقط destructor.
// C#public class Resource: IDisposable{public void Dispose(){// release resource}} // C++/CLIpublic ref class Resource{public:~Resource(){// release resource}}; ' Visual BasicPublic Class ResourceImplements IDisposablePublic Sub Dispose() Implements IDisposable.Dispose' release resourceEnd SubEnd Class // F#type Resource() as this =interface IDisposable withmember this.Dispose() = printfn "release resource" ملاحظة : يتم استخدام ميثود Dispose() مع C++/CLI عن طريق جملة delete.
- جملة using
تعمل جملة using في C# بتسلسل aquire/use/release من اجل تحرير الموارد عندما لا تعد مستعملة حتى لو في حالة exception . الكومبايلر ينشأ جملة try/finally ويستدعي ميثود Dispose داخل finally . تقدم فيجوال بيسك جملة using مثل C# اما C++/CLI فتمتلك اسلوب اكثر اناقة لهذه المشكلة . فاذا كان نوع reference معرف محلياً فالكومبايلر سينشأ جملة try/catch من اجل استدعاء ميثود Dispose() في نهاية نطاق الكود.
تقدم F# بنائين مختلفين لدعم تحرير الموارد بسهولة. كلمة use تلزم باستدعاء ميثود Dispose بشكل تلقائي عندما تخرج القيمة عن النطاق. اما تعبير using فينشأ object يجب التخلص منه و ويتم التخلص منه في الفنكشن المستدعى.
هنا الفنكشن test يتم استدعاءه بعد انشاء كائن Respource.
// C#using (Resource r = new Resource()){r.test();}// C++/CLI{Resource r;r.test();}' Visual BasicUsing r As New Resourcer.test()End Using// F# use bindinglet rs =use r = new Resource()r.test()// r.Dispose called implicitly// F# using expressionlet test2 (r : Resource) =r.test()using (new Resource()) test2 Override finalize -
اذا كان الكلاس يحتوي على موارد محلية من الضروري أن تحرر فيجب عليه عمل override لميثود Finalize() من كلاس object.
يتم عمل ذلك في C# عن طريق كتابة destructor اما C++/CLI فتمتلك نمط كتابي خاص مع بادئة ! من اجل تعريف finalizer.
ضمن الfinalizer لا يمكنك التخلص من كائنات تحتوي على finalizer ايضاً لأن ترتيب الfinalization غير مضمون .
لهذا السبب تعرف Dispose ميثود اضافية Dispose() مع باراميتر Boolean .
اما C++/CLI فليس من الضروري عمل implement لهذا النمط في الكودات لأن هذا يتم من قبل الكومبايلر . الdestructor في C++/CLI يعمل implement لكتلا دوال الDispose.
في لغة الفيجوال بيسك يجب عمل implement لكل من Dispose() و الfinalizer بشمل يدوي. على أي حال اغلب الclasses في الفيجوال بيسك لا تستعمل الموارد المحلية بشكل مباشر . حيث إنها تستعملها بمساعة classes اخرى . ايضاً مع فيجوال بيسك عادة ليس من الضروري عمل override لميثود Finalize() لكن عمل implement لميثود Dispose() مطلوب .
ملاحظة : كتابة destructor مع C# يعمل override لميثود Finalize() للbase class .
يقوم الdestructor بعمل implement للانترفيس IDisposable في C++/CLI.
// C#public class Resource: IDisposable{~Resource // override Finalize{Dispose(false);}protected virtual void Dispose(bool disposing){if (disposing) // dispose embedded members{}// release resources of this classGC.SuppressFinalize(this);}public void Dispose(){Dispose(true);}}// C++/CLIpublic ref class Resource{public:~Resource() // implement IDisposable{this->!Resource();}!Resource() // override Finalize{// release resource}}; ' Visual BasicPublic Class ResourceImplements IDisposablePublic Sub Dispose() Implements IDisposable.DisposeDispose(True)GC.SuppressFinalize(Me)End SubProtected Overridable Sub Dispose(ByVal disposing)If disposing Then' Release embedded resourcesEnd If' Release resources of this classEnd SubProtected Overrides Sub Finalize()TryDispose(False)FinallyMyBase.Finalize()End TryEnd SubEnd Class DELEGATES
الDelegates هي نوع امن من الpointers للmethods. في جميع اللغات الأربع , يمكن استعمال الكلمة المحجوزة delegate من اجل تعريف delegate . الإختلاف هو في كيفية استعمال الdelegate.
المثال التالي يوضح كلاس Test مع ميثود Test1() التي هي static وميثود Test2() التي هي instance . كل من هذه الmethods يتم استدعائهم بنموذج delegate من نوع TestDelegate .
تم تعريف TestDelegate من اجل استدعاء ميثود ترجع قيمة void ومع باراميتر int.
عند استعمال الDelegate تدعم C استدلال الDelegate حيث ان الكومبايلر ينشأ نموذج من الdelegate ويمرر عنوان الميثود .
يمكن عمل دمج للdelegates عن طريق العملية + في C# و C++/CLI.
// C#public delegate void TestDelegate(int x);public class Test{public static void Test1(int x) { }public void Test2(int x) { }}Test de = new Test();TestDelegate de1 = Test.Test1;TestDelegate de2 = de.Test2;TestDelegate de3 = de1 + de2;de3(11); استدلال delegate غير ممكن مع C++/CLI حيث انه يجب عليك انشاء instance جديد من نوع delegate ثم تمرر عنوان الميثود للconstructor.
// C++/CLIpublic delegate void TestDelegate(int x);public ref class Test{public:static void Test1(int x) { }void Test2(int x) { }};Test^ de = gcnew Test();TestDelegate^ de1 = gcnew TestDelegate(&Test:: Test1);TestDelegate^ de2 = gcnew TestDelegate(de, &Test:: Test2);TestDelegate^ de3 = de1 + de2;de3(11); على غرار C++/CLI لا تدعم لغة الفيجوال بيسك استدلال delegate ف يجب عليك انشاء instance جديد من نوع delegate ثم تمرر عنوان الميثود . تمتلك لغة الفيجوال بيسك اداة AddressOf من اجل تمرير عنوان الميثود .
يجب أن تدرك ايضاً ان لغة الفيجوال بيسك لا تعمل override لعملية + للdelegates لذلك من الضروري استدعاء ميثود Combine() من كلاس delegate.
لاحظ أن كلاس Delegate مكتوب داخل اقاس لأن كلمة Delegate هي كلمة محجوزة في الفيجوال بيسك وبالتالي ليس من الممكن استعمال كلاس بنفس الإسم فوضع اقواس حول Delegate يؤكد أن الكلاس هو الذي نريده وليس الكلمة المحجوزة Delegate.
' Visual BasicPublic Delegate Sub TestDelegate(ByVal x As Integer)Public Class TestPublic Shared Sub Test1(ByVal x As Integer)'End SubPublic Sub Test2(ByVal x As Integer)'End SubEnd ClassDim de As New Test()Dim de1 As New TestDelegate(AddressOf Test. Test1)Dim de2 As New TestDelegate(AddressOf de. Test2)Dim de3 As TestDelegate = [Delegate].Combine(de1, de2)de3(11) الفنكشنات مثل الobjects مواطنين درجة اولى first-class في F# . فالdelegates مطلوبة كما مع interoperability من لغات دوت نت الاخرى . لذلك النمط الكتابي يبدو معقد اكثر.
يتم تعريف الdelegates في F# باعطاء الكلمة المحجوزة delegate إلى الtype name ومن ثم تعريف الباراميترز والنوع المرجع returned type.
في المثال التالي نوع الباراميتر في TestDelegate هو int ونوع القيمة المرجعة unit التي هي مشابهة للvoid في C#.
نوع Test تم تعريف مع static member والذي هو Test1 ومع instance member والذي هو Test2. كل من هؤلاء الأعضاء يوفون بمتطلبات نوع الdelegate.
المتغير de1 هو متغير delegate يؤشر على الميثود Test.Test1 و de2 يؤشر على الميثود Test2 والذي هو instance.
تم تعريف الفنكشن test-invokeDelegate مع الباراميترز TestDelegate و int من اجل استدعاء الفنكشنات المؤشر عليها من قبل الdelegate. يتم استدعاء الميثود invoke لكلاس الdelegate ويمرر باراميتر int بعد تعريف الفنكشن test-invokeDelegate ويتم استدعاءه عن طريق تمرير نموضج الdelegate de1 والقيمة 46 .
من اجل دمج الDelegates تحتاج لاستدعاء الميثود Delegate.Combine() لأان ميثود Combine() تحتاج 2 باراميترز من نوع Delegate ثم ترجع نوع Delegate.
هنا النمط الكتابي لF# .. :> يستعمل لاختيار de1 نسبة إلى النوع الأساسي Delegate اما :?> تستعمل لاختيار نوع Delegate de33 نسبة للكلاس المشتق TestDelegate .
يمكن ايضاً استعمال الكلمات المحجوزة upcast و downcast بدلا من استعمال :> و :?> .
// F#type TestDelegate = delegate of int -> unittype Test() as this =static member Test1(x : int) =printfn " Test1 %d" xmember this. Test2(x : int) =printfn " Test2 %d" x// F# using delegatelet de = Test()let de1 : TestDelegate = new TestDelegate(Test. Test1)let invokeDelegate (delg : TestDelegate) (x : int) =delg.Invoke(x)(invokeDelegate de1 46)let de2 : TestDelegate = new TestDelegate(de. Test2)let de11 = de1 :> Delegatelet de22 = de2 :> Delegatelet de33 : Delegate = Delegate.Combine(de11, de22)let de3 : de33 :?> TestDelegate(invokeDelegate de3 11) EVENTS
يمكن عمل الية subscription تعتمد على delegates مع الكلمة المحجوزة event.
كل اللغات تعرف الكلمة المحجوزة event من اجل تقديم events من الكلاس. كلاس EventTest يطلق events مع الاسم TestEvent من نوع TestDelegate.
النمط الكتابي لعمل اطلاق للevent في C# يبدو مشابة لاستدعاء ميثود الevent. متغير الevent هو null طالما لا شيئ مربوط او مسجل به لذلك يجب أن يتم فحص للnot null قبل أن يتم اطلاق الevent.
ميثود الhandler مسجلة ومربوطة باستعمال العملية += ويتم تمريره بمساعدة استدلال الdelegates.
// C#public class EventTest{public event TestDelegate TestEvent;public void FireEvent(){if (TestEvent != null)TestEvent(44);}}public class Subscriber{public void Handler(int x){// handler implementation}}//...EventTest evd = new EventTest();Subscriber subscr = new Subscriber();evd.TestEvent += subscr.Handler;evd.FireEvent(); لغة C++/CLI مشابهة جداً في هذا الأمر للC# إلا انه عند اطلاق الevent لا تحتاج لفحص اذا ما كان الevent هو null ام لا . لأان هذا يتم بشكل تلقائي عن طريق كود IL المنشأ من قبل الكومبايلر .
ملاحظة : لغتا C# و C++/CLI تستعمل العملية += للازالة من الevent ايضاً.
// C++/CLIpublic ref class EventTest{public:event TestDelegate^ TestEvent;public void FireEvent(){TestEvent(44);}}public class Subscriber{public:void Handler(int x){// handler implementation}}//...EventTest^ evd = gcnew EventTest();Subscriber^ subscr = gcnew Subscriber();evd - > TestEvent += gcnew TestDelegate(subscr, & Subscriber::Handler);evd - > FireEvent(); تمتلك لغة فيجوال بيسك نمط كتابي مختلف حيث يتم تعريف الevent عن طريق الكلمة المحجوزة Event التي هي مثل C# و C++/CLI . لكن اطلاق الevent يتم عن طريق جملة RaiseEvent التي تفحص اذا ما كان متغير الevent تم تهيئته من قبل الsubscriber.
تقوم جملة AddHandler باضافة وتسجيل handler مثل نفس الوظيفة التي تقدمها عملية += في C#.
تحتاج AddHandler إلى اثنان من الباراميترز . الأول يعرف الevent والثاني عنوان الhandler.
تستعمل جملة RemoverHandler لازالة handler من الevent .
' Visual BasicPublic Class EventTestPublic Event TestEvent As TestDelegatepublic Sub FireEvent()RaiseEvent TestEvent(44);End SubEnd ClassPublic Class SubscriberPublic Sub Handler(ByVal x As Integer)' handler implementationEnd SubEnd Class'...Dim evd As New EventTest()Dim subscr As New Subscriber()AddHandler evd.TestEvent, AddressOf subscr.Handlerevd.FireEvent() تقدم لغة فيجوال بيسك نمط كتابي اخر غير موجود في اللغات الخرى حيث يمكنك استعمال الكلمةالمحجوزة handler مع الميثود المشاركة مع الevent . حتى تعمل ذلك يجب أن تعرف متغير عن طريق الكلمة المحجوزة WithEvents.
Public Class SubscriberPublic WithEvents evd As EventTestPublic Sub Handler(ByVal x As Integer) Handles evd.TestEvent' Handler implementationEnd SubPublic Sub Action()evd = New EventTest()evd.FireEvent()End SubEnd Class GENERICS
جميع اللغات الأربع تدعم انشاء واستعمال الgenerics. وحتى نستعملها اقترضت C# النمط الكتابي من C++ templates من اجل تعريف نوع generic مع اقواس محدبة على شككل زاوية . ايضا C++/CLI تستعمل نفس الشكل.
في لغة الفيجوال بيسك يتم تعريف نوع generic عن طريق الكلمة المحجوزة Of في الأقواس ,
هنا ايضاً تبدو F# مشابهة جداً لل C#.
// C#List<int> intList = new List<int>();intList.Add(1);intList.Add(2);intList.Add(3); // C++/CLIList<int>^ intList = gcnew List<int>();intList->Add(1);intList->Add(2);intList->Add(3); ' Visual BasicDim intList As List(Of Integer) = New List(Of Integer)()intList.Add(1)intList.Add(2)intList.Add(3) // F#let intList =new List<int>()intList.Add(1)intList.Add(2)intList.Add(3) هنا يعلم الكومبايلر انك تريد انشاء نوع generic لانك استعملت اقواس على شكل زاوية مع التصريح عن الكلاس.
القيود يتم تعريفها في جملة where كالمثال التالي:
public class MyGeneric<T>where T: IComparable<T>{private List<T> list = new List<T>();public void Add(T item){list.Add(item);}public void Sort(){list.Sort();}} تعريف نوع generic مع C++/CLI مشابه جداً لتعريف templates مع C++ فبدلاً من الكلمة المحجوزة template يتم استعمال الكلمة المحجوزة generic . بالنسبة لجملة where فهي مشابهة للموجودة في C# مع العلم أن C++/CLI لا تدعم وضع قيود على الConstructor.
generic <typename T>where T: IComparable<T>ref class MyGeneric{private:List<T>^ list;public:MyGeneric(){list = gcnew List<T>();}void Add(T item){list->Add(item);}void Sort(){list->Sort();}}; لغة فيجوال بيسك تعرف كلاس الgeneric عن طريق كتابة الكلمة المحجوزة Of والقيود يمكن تعريفها مع As . لاحظ المثال التالي:
Public Class MyGeneric(Of T As IComparable(Of T))Private myList = New List(Of T)Public Sub Add(ByVal item As T)myList.Add(item)End SubPublic Sub Sort()myList.Sort()End SubEnd Class تعرف F# نوع generic باستعمال اقواس على شكل زاوية ونوع generic يتم تمييزه بعملامة التنصيص الاحادية ‘.
القيود تعرف باستعمال الكلمة المحجوزة when والمحدد يوضع بعدها . في المثال التالي تم استعمال محدد النوع ب :> هذا يعرف أن نوع generic يجب أن يشتق من انترفيس او base class . ويمكنك ايضا تعريف محددات من نوع value
(: struct) وايضاً قيود من انواع reference (: not struct) و قيود الconstructor التلقائي
(‘a : (new : unit -> ’a)) . المحدد الغير متوفر في اللغات الاخرى هو محدد null (‘a : null) هذا المحدد يعرف أن نوع الgeneric ‘a يحتاج أن يكون قابل لقيمة null وهذا يسمح لكل انواع reference لكن يمنع انواع F# التي هي قيم غير قابلة لتكون null.
type MyGeneric < 'a > when 'a : > IComparable < 'a > () as this =let list = new List < 'a > ()member this.Add(item : 'a) = list.Add(item)member this.Sort() = list.Sort() استعلامات LINQ
استعمالات Language-integrated هي ميزة هي احدى ميزات الC# والفيجوال بيسك . النمط الكتابي متشابه جدا بين هذه اللغات
var query = from r in teamswhere r.Country == "Brazil"orderby r.Wins descendingselect r; ' Visual BasicDim query = From r in teams _Where r.Country = "Brazil" _Order By r.Wins Descending _Select r ملاحظة : لغة C++/CLI لا تدعم استعلامات LINQ .
خلط كودات Managed مع كودات Native في C++/CLI
احد اكبر المميزات لC++/CLI هو القدرة على خلط كودات Managed مع كودات Native ففي كلاس الmanaged يمكنك كتابة كودات Managed مع كودات Native كما ترى في المثال . نفس الشيئ لكلاس native حيث يمكنك كتابة كودات Managed مع كودات Native ضمن ميثود واحد.
في كلاس managed يمكنك ايضا تعريف field من نوع native أو Pointer على نوع native . فعل هذا بالعكس لا يمكن تحقيقه مباشره فيجب عليك اخذ الحيطة !. لأن الinstance من نوع managed يمكن أن يزال عن طريق الgarbage collector عند تنظيف الذاكرة.
لكي نستعمل كلاس من نوع managed كmember ضمن native classes تعرف لغة C++/CLI الكلمة المحجوزة gcroot والتي هي معرفة في ملف الهيدر gcroot.h . هذه الكلمة تغطي الGCHandler التي تحفظ كائن CLR من native reference .
#pragma once#include < iostream > // include this header file for coutusing namespace std; // the iostream header defines the namespace stdusing namespace System;public ref class Managed{public:void MixNativeAndManaged(){cout << "Native Code" << endl;Console::WriteLine("Managed Code");}}; خصائص C#
هناك بعض مميزات النمط الكتابي لC# التي لم نغطيها في هذه المقالة . مثلاً تعرف C# جملة yield التي تسهل انشاء الenumerators. وهذه الجملة غير موجودة في الفيجوال بيسك وC++/CLI حيث ان الEnumerator في هذه اللغات يجب أن يكتب بشكل يدوي.
ايضا تقدم C# شكل خاص لانواع nullable في حين مع اللغات الاخرى يجب عليك استعمال generic struct Nullable<T> بدلا من ذلك .
تسمح لغة C# بعمل blocks لكودات unsafe حيث يمكنك استعمال الPointers و Pointer arithmetic ^ .
هذه الميزة يمكن أن تكون مفيدة للغاية لاستدعاء methods من مكتبات native . لغة الفيجوال بيسك لا تمتلك هذه القدرة هذا بالفعل تفوق حقيقي للC# !.
اما C++/CLI ليست بحاجة للكلمة المحجوزة unsafe حتى تعرف كودات unsafe لأنه من الطبيعي جداً خلط انواع الmanaged مع الnative فيها .
هناك نقطة اخرى عن VB وهي أن ترجمة الكودات الفيجوال بيسك تكون في الخلفة background فبينما هذا يعتبر رائع للمشاريع الصغيرة إلا أن عديد من الأشخاص الذين يعملون على مشاريع كبيرة جداً وجدوا ان IDE يصبح ابطأ كلما زاد حجم المشروع .
خلاصة المقال
في هذا المقال تعلمنا كيفية تحويل كودات C# إلى كودات الفيجوال بيسك وC++/CLI و F# .
لغة C++/CLI تضيف ملحقات للC++ من اجل كتابة تطبيقات الدوت نت وتستند على النمط الكتابي لC# في بناء الجمل .
على الرغم من أن C# و C++/CLI يمتلكون نفس الجذور إلا ان هناك اختلافات مهمة بينهما .
لغة الفيجوال بيسك لا تستعمل الأقواس المجعدة لكن بدلا من ذلك تستعمل مقاتيح خاصة لفتح واغلاق الجمل البرمجية.
النمط الكتابي للغة F# يختلف بشكل كبير لأنه يركز بشكل رئيسي على البرمجة الوظيفية وليس البرمجة كائنية التوجه (مع دعمه لها بطبيعة الحال).
اخذنا في هذه المقالة لمحة عامة عن كيفية كتابة اللغات الاخرى من تعريف للانواع والmethods والproperties وتعرفنا ايضا على الكلمات المحجوزة التي تستعمل في البرمجة كائنية التوجه . وايضا اخذنا فكرة عن كيفية تحرير الموارد وكيفية تطبيق وعمل delegates و events و generics في اللغات الأربع.
على الرغم من امكانية تحويل معظم النصوص البرمجية إلى أن اللغات ما زالت تحتفظ بفروقات في وظائفها .
عبد العظيم بخاري
(تم وضع الموضوع في عدة منتديات عربية + يرجى حفظ الحقوق عند النقل)