قائمة شاملة بالفروق بين VB.NET و C #. الجزء 1

صورة

وفقًا لتصنيف TIOBE في عام 2018 ، تفوقت VB.NET على شعبية C #. صدفة أم لا ، ولكن في فبراير / شباط ، حث إريك ليبيرت ، أحد المبدعين في C # ، القراء على الانتباه إلى مدونة صديقه ، وهو زميل سابق في برنامج التحويل البرمجي لـ Roslyn ، وفي نفس الوقت ، وهو من محبي VB.NET المتحمسين ، أنتوني جرين . "هذه الموارد هي تفاصيل متعمقة من خبراء ليس من السهل العثور عليهم من خلال قراءة الوثائق" ، كتب إريك. نقدم انتباهكم إلى الجزء الأول من ترجمة مقالة أنتوني جرين "قائمة شاملة بالفروق بين VB.NET و C #". ربما في هذه الاختلافات بالتحديد يكمن سر ديناميكيات تصنيف هذه اللغات.

طوال نصف حياتي تقريبًا ، شهدت وشاركت في مناقشات لا حصر لها حول مدى تشابه أو اختلاف لغتي .NET الأكثر شيوعًا. أولاً ، بصفتي أحد الهواة ، ثم كمحترف ، وأخيراً ، كمدافع عن العملاء ومدير برنامج ومصمم لغة ، أستطيع أن أقول دون مبالغة عدد المرات التي سمعت أو قرأت شيئًا مثل:
"... VB.NET هو مجرد طبقة رقيقة أعلى IL ، مثل C # ..."
أو
"... VB.NET هو في الواقع مجرد C # مع عدم وجود فواصل منقوطة ..."
كما لو كانت اللغات عبارة عن تحويل XML أو ورقة أنماط.

وإذا كان بعض الزائرين المتحمسين لا يكتب هذا في تعليق ، فغالبًا ما يتم تضمين ذلك في أسئلة مثل: "مرحبًا ، أنتوني! صادفت هذا الفارق الصغير في مكان واحد - هل هذا خطأ؟ كيف يمكن لهاتين اللغتين المتماثلتين ، والتي يجب أن تكون متطابقة باسم كل الخير والمقدسين في هذا العالم ، أن تتفرق في هذا المكان الواحد؟ لماذا نحتاج إلى مثل هذا الظلم؟!

" منفصلة " ، كما لو كانت هي نفسها حتى حدثت طفرة ، ثم أصبحت فصيلة منفصلة. هاه!

لكنني أفهم ذلك. قبل انضمامي إلى Microsoft ، ربما أكون متمسكًا بهذه الفكرة بشكل غامض واستخدمتها كحجة للرد على المعارضين أو لطمأنة شخص ما. أنا أفهم سحرها. من السهل أن نفهم وسهل التكرار. لكن العمل على Roslyn (إعادة كتابة VB و C # بالكامل من البداية) لمدة 5 سنوات ، أدركت مدى خطأ هذه الفكرة بشكل لا لبس فيه . لقد عملت مع فريق من المطورين والمختبرين لإعادة تنفيذ كل شبر من كلتا اللغتين ، بالإضافة إلى أدواتهم في حل ضخم متعدد المشروعات مع ملايين سطور التعليمات البرمجية المكتوبة باللغتين. ومع الأخذ في الاعتبار العدد الكبير من المطورين الذين يتحولون بينهم ذهابًا وإيابًا ، والمستوى العالي من التوافق مع نتائج وخبرات الإصدارات السابقة ، بالإضافة إلى الحاجة إلى إعادة إنتاج حجم ضخم لواجهة برمجة التطبيقات (API) بتفصيل كبير ، فقد اضطررت إلى معرفة الاختلافات عن كثب. في الواقع ، يبدو لي أحيانًا أنني تعلمت شيئًا جديدًا حول VB.NET (لغتي المفضلة) كل يوم.

وأخيرًا ، قضيت بعض الوقت للجلوس وتفريغ جزيء من المخ تعلمته باستخدام VB.NET وخلقه على مدار الخمسة عشر عامًا الماضية ، على أمل أن أتمكن من توفير وقتي على الأقل في المرة القادمة.

قبل الانتقال إلى القائمة ، سأوجز القواعد الأساسية:

  • هذه القائمة ليست شاملة بالمعنى المعتاد. انه شامل. هذه ليست كل الاختلافات. هذه ليست جميع الاختلافات التي أعرفها عمومًا. هذه مجرد اختلافات يمكنني تذكرها أولاً ، حتى أشعر بالتعب الشديد للمتابعة ؛ حتى نفدت قوتها. إذا التقيت أنا أو أي منكم أو تذكرت اختلافات أخرى ، فسيسعدني تحديث هذه القائمة.
  • سأبدأ في بداية مواصفات VB 11 وأتجه لأسفل باستخدام محتوياته لتذكير نفسي بالاختلافات التي تتبادر إلى الذهن أولاً بشأن هذا الموضوع.
  • هذه ليست قائمة الوظائف في VB غير الموجودة في C #. لذلك لا "XML حرفي مقابل المؤشرات." هذا أمر شائع جدًا ، وهناك بالفعل العديد من هذه القوائم على الإنترنت (بعضها كتبه لي ، وربما سأكتب في المستقبل أكثر). سوف أركز في المقام الأول على الإنشاءات التي لها تناظرية في كلتا اللغتين ، وحيث قد يشير أحد المراقبين غير المطلعين إلى أن هذين الأمرين يتصرفان بنفس الطريقة ، ولكن هناك اختلافات صغيرة أو كبيرة ؛ قد تبدو هي نفسها ، ولكنها تعمل بشكل مختلف أو في النهاية تنشئ كودًا مختلفًا.
  • هذه ليست قائمة بالفروق النحوية بين VB و C # (والتي لا تعد ولا تحصى). سأتحدث بشكل أساسي عن الاختلافات الدلالية (ما تعنيه الأشياء) ، وليس عن الاختلافات النحوية (كيف تتم كتابة الأشياء). لذلك لا توجد أجزاء مثل "VB تبدأ التعليقات بـ" ، و C # تستخدم // "أو" في C # _ هو معرف صالح ، ولكن ليس في VB ". لكنني سوف كسر هذه القاعدة لعدة حالات. بعد كل شيء ، القسم الأول من المواصفات يدور حول القواعد المعجمية.
  • في كثير من الأحيان ، سأقدم أمثلة ، وأحيانًا سأقدم مبررات لماذا يمكن أن يذهب التصميم بطريقة أو بأخرى. تم اتخاذ بعض قرارات التصميم أمام عيني ، لكن الغالبية العظمى سبقت وقتي ، ولا يمكنني إلا أن أخمن سبب اتخاذها.
  • يرجى ترك تعليق أو تغريدة لي ( ThatVBGuy ) لإعلامي بالاختلافات المفضلة لديك و / أو تلك التي ترغب في معرفة المزيد عنها.

بعد تحديد التوقعات ودون مزيد من التأخير ...

المحتويات


النص المخفي

بناء الجملة والمعالجة المسبقة



الإعلانات ، الخ



تعليمات



بناء الجملة والمعالجة المسبقة


1. يمكن لكلمات VB والمشغلين استخدام أحرف كاملة العرض


بعض اللغات (لا أعرف عددها بالضبط ، ولكن على الأقل في بعض أشكال الصينية واليابانية والكورية) تستخدم أحرفًا كاملة العرض. باختصار ، هذا يعني أنه عند استخدام خط أحادي المسافة (كما يفعل معظم المبرمجين) ، فإن الحرف الصيني سيشغل ضعف المساحة الأفقية مثل الأحرف اللاتينية التي اعتدنا رؤيتها في الغرب. على سبيل المثال:



هنا لدي إعلان متغير مكتوب باللغة اليابانية ، والتهيئة بسلسلة مكتوبة أيضًا باللغة اليابانية. وفقًا لمترجم Bing ، يُطلق على المتغير "تحية" والخط يقول "Hello World!" يبلغ طول اسم المتغير باللغة اليابانية حرفين فقط ، ولكنه يشغل مساحة تتكون من 4 أحرف نصف العرض التي تعطيها لوحة المفاتيح بشكل طبيعي ، كما يوضح التعليق الأول. هناك إصدارات كاملة العرض من الأرقام وجميع حروف ASCII المطبوعة الأخرى التي لها نفس عرض الأحرف اليابانية. لإثبات ذلك ، كتبت تعليقًا ثانيًا باستخدام أرقام العرض الكامل "1" و "2". هذه ليست "1" و "2" كما في التعليق الأول. لا توجد مسافات بين الأرقام. يمكنك أيضًا أن ترى أن حجم الأحرف ليس بعرض حرفين بالضبط ، وهناك إزاحة بسيطة. هذا جزئيًا لأن هذا البرنامج يمزج بين الأحرف ذات العرض الكامل ونصف العرض في سطر واحد وفي الأسطر الثلاثة.

المسافات نصف العرض ، الأحرف الأبجدية الرقمية كاملة العرض. نحن لسنا مبرمجين إلا إذا كنا مهووسين بمحاذاة النص. ويبدو لي أنك إذا كنت من الصينيين أو اليابانيين أو الكوريين (أو أي شخص آخر يستخدم أحرفًا كاملة الحجم بلغتهم) وتستخدم معرفات أو سلاسل مكتوبة بلغتهم الأم ، فإن أخطاء المحاذاة البسيطة هذه تغضب.

بقدر ما أفهم ، اعتمادًا على لوحة المفاتيح اليابانية ، يكون التبديل بين الهيروغليفية واللاتينية أمرًا سهلاً ، لكن من الأفضل استخدام أحرف لاتينية كاملة العرض. يدعم VB هذا في الكلمات الأساسية والمسافات والمشغلين وحتى علامات الاقتباس. لذلك كل هذا يمكن أن يكتب مثل هذا:



كما ترون ، في هذا الإصدار ، تستخدم الكلمات الأساسية والمسافات والتعليقات والمشغلات وحتى علامات الاقتباس إصداراتها كاملة الحجم. في الفوضى جلبت النظام.

نعم. استخدام اليابانية VB. في الواقع ، على الرغم من بناء الجملة المشابه للغة الإنجليزية (وربما هذا هو السبب) ، بالنسبة لمعظم مستخدمي VB الذين أراهم في المنتديات ، فإن اللغة الإنجليزية ليست هي اللغة الرئيسية. خلال عملي في Microsoft ، قابلت اليابانية VB MVP عدة مرات ، واحد منهم على الأقل جلب باستمرار الحلويات اليابانية. إذا كنت مبرمجًا VB من الصين أو اليابان أو كوريا (أو من أي بلد آخر يستخدم أحرفًا كاملة العرض) ، فيرجى كتابة التعليقات. (في التعليقات على المؤلف ، كتبوا أن اليابانيين يحاولون في كل مكان في الكود استخدام أسكي - تقريبا. ) .

لحظة مضحكة: عندما طبقت في البداية خطوطًا محرفًا في VB ، لم أكن لأخذ (في حيرتي) في الحسبان إمكانية وجود أقواس مجعدة كاملة العرض في أماكن الاستبدال. اكتشف فلاديمير ريشيتنيكوف ( vreshetnikov ) هذا الخطأ وتصحيحه ، بحيث بقي التقليد العظيم للتسامح VB على عرض الأحرف ساري المفعول.

2. VB يدعم اقتباسات ذكية


حسنًا ، هذا بالطبع تافه ، لكن جدير بالذكر. هل سبق لك أن رأيت نموذج التعليمة البرمجية في مستند نصي مثل هذا:



وبعد نسخ المثال في التعليمة البرمجية ، هل وجدت أن أياً من علامات الاقتباس (المميزة) لا تعمل ، لأن Word يستبدل كل علامات اقتباس ASCII المعتادة بـ علامات اقتباس ذكية ؟

انا لا. حسنًا ، كان لدي ، ولكن فقط عندما قمت بنسخ الأمثلة إلى C #. في VB ، تكون علامات الاقتباس الذكية بمثابة محددات صالحة للسلاسل (من المضحك أن علامات الاقتباس الروسية «» لا تعمل - تقريبًا.):



كما أنها تعمل داخل الأوتار ، على الرغم من أنها ربما بطريقة غريبة. إذا قمت بمضاعفة علامات الاقتباس الذكية للهروب ، فكل ما تحصل عليه في وقت التشغيل هو مجرد علامة اقتباس واضحة ("غبية"). قد يبدو هذا غريبًا بعض الشيء ، ولكنه مع ذلك عملي جدًا ، حيث يُسمح باستخدام علامات اقتباس ذكية في أي مكان تقريبًا في السلسلة. لا يجعلك المترجم ينتهي بك بالتأكيد إلى عرض أسعار ذكي أو يستخدم السعر الصحيح إذا بدأت مع اقتباس ذكي ، لذلك يمكنك مزجه بدون قلق. ونعم ، هذا يعمل أيضًا مع حرف التسعير الفردي المستخدم للتعليقات:



حاولت أن أجعل Paul Wick ( panopticoncntrl ) يعترف بأنه فعل ذلك فقط لأنه كان يعذب هذه المشكلة أثناء العمل على المواصفات ، لكنه ينكر أي ذنب. لم يكن هذا هو الحال في VB6 ، لذلك أضاف شخص ما هذا لاحقًا.

3. يمكن أن تكون ثوابت المعالجة قبل أي نوع بدائي (بما في ذلك التواريخ) وقد تحتوي على أي قيمة ثابتة



4. يمكن استخدام العوامل الحسابية في تعبيرات المعالجة المسبقة




الإعلانات ، الخ


5. VB يتخطى أحيانًا IL ينفذ الإعلانات لمنع التنفيذ الضمني العرضي للواجهة بالاسم.


هذا البند هو من فئة الباطنية. في VB ، يتم تنفيذ الواجهة دائمًا بشكل صريح. لكن اتضح أنه في حالة عدم وجود تطبيق صريح ، فإن السلوك الافتراضي لـ CLR عند استدعاء طريقة واجهة هو البحث عن الأساليب العامة بالاسم والتوقيع. في معظم الحالات ، يكون هذا أمرًا طبيعيًا ، لأنه في VB عادة ما تحتاج إلى توفير تطبيق لكل عضو في الواجهة التي تقوم بتطبيقها ، باستثناء حالة واحدة:

 Interface IFoo Sub Bar() Sub Baz() End Interface Class Foo Implements IFoo Private Sub Bar() Implements IFoo.Bar Exit Sub End Sub Private Sub IFoo_Baz() Implements IFoo.Baz Exit Sub End Sub End Class Class FooDerived Inherits Foo Implements IFoo Public Sub Bar() Implements IFoo.Bar Exit Sub End Sub Public Sub Baz() ' Does something unrelated to what an IFoo.Baz would do. End Sub End Class 

gist.github.com/AnthonyDGreen/39634fd98a0cacc093719ab62d7ab1e6#file-partial-re-implementation-vb

في هذا المثال ، FooDerived فئة FooDerived فقط إعادة تعيين IFoo.Bar إلى الطريقة الجديدة ، مع ترك التطبيقات المتبقية دون تغيير. اتضح أنه إذا كان المترجم يقوم ببساطة بإنشاء التوجيه لتنفيذ FooDerived ، فإن CLR FooDerived.Baz كتطبيق جديد لـ IFoo.Baz (على الرغم من أنه في هذا المثال لا يرتبط بـ IFoo ). في C # ، يحدث هذا ضمنيًا (ولست متأكدًا مما إذا كان بإمكاني رفضه) ، ولكن في VB ، قام المحول البرمجي في الواقع بحذف "الأدوات" من الإعلان بأكمله لتجنب ذلك ، ويتجاهل فقط الأعضاء المعينين الذين تم إعادة تطبيقهم. بمعنى آخر ، إذا سألت FooDerived عما إذا كان IFoo مباشرة ، فسوف يقول لا:


لماذا أعرف هذا ولماذا هو مهم؟ لسنوات عديدة ، طلب مستخدمو VB دعمًا للتطبيق الضمني للواجهة (دون تحديد الأدوات بشكل صريح في كل إعلان) ، عادةً لإنشاء التعليمات البرمجية. مجرد دمج هذا مع بناء الجملة الحالي سيكون كسر التغيير ، لأن FooDerived.Baz الآن ينفذ ضمنيًا IFoo.Baz ، على الرغم من أنه لم يفعل ذلك من قبل. لكن في الآونة الأخيرة ، تعلمت المزيد حول هذا السلوك عند مناقشة مشكلات التصميم المحتملة مع ميزة "تنفيذ الواجهة الافتراضية" ، والتي من شأنها أن تسمح للواجهات بتضمين التطبيقات الافتراضية لبعض الأعضاء ولا تتطلب إعادة التنفيذ في كل فئة. قد يكون ذلك مفيدًا للحمل الزائد ، على سبيل المثال ، عندما يكون التنفيذ من المحتمل أن يكون هو نفسه بالنسبة لجميع المنفذين (التفويض إلى التحميل الزائد الرئيسي). سيناريو آخر هو الإصدار. إذا كان يمكن أن تتضمن الواجهة تطبيقات افتراضية ، فيمكنك إضافة أعضاء جدد إليها دون كسر التطبيقات القديمة. ولكن هناك مشكلة. نظرًا لأن السلوك الافتراضي في CLR هو البحث عن تطبيقات عامة بالاسم والتوقيع ، إذا كانت فئة VB لا تطبق أعضاء الواجهة مع تطبيقات افتراضية ، ولكن لديها أعضاء عامين لديهم اسم وتوقيع مناسبين ، فإنهم يقومون ضمنيًا بتطبيق أعضاء الواجهة هؤلاء ، حتى إذا قمت بذلك بالكامل ليس من المفترض أن. هناك أشياء يمكنك القيام بها للتغلب على هذا عندما تكون المجموعة الكاملة من أعضاء الواجهة معروفة في وقت الترجمة. ولكن في حالة إضافة العضو بعد تجميع الشفرة ، فإنه يغير السلوك بصمت في وقت التشغيل.

6. VB بشكل افتراضي يخفي أعضاء الفئة الأساسية بالاسم (الظلال) ، وليس بالاسم والتوقيع (الأحمال الزائدة)


أعتقد أن هذا الاختلاف معروف جيدًا. السيناريو هو هذا: أن ترث الفئة الأساسية ( DomainObject ) ، وربما خارجة عن DomainObject ، وتعلن طريقة لها اسم منطقي في سياق الفصل الدراسي ، على سبيل المثال ، Print :

 Class DomainObject End Class Class Invoice Inherits DomainObject Public Sub Print(copies As Integer) ' Sends contents of invoice to default printer. End Sub End Class 

gist.github.com/AnthonyDGreen/863cfd1e7536fe8bda7cd145795eaf9f#file-shadows-example-vb

حقيقة أن الفاتورة يمكن طباعتها أمر منطقي. ولكن في الإصدار التالي من واجهة برمجة التطبيقات (API) حيث يتم الإعلان عن الفئة الأساسية الخاصة بك ، يقررون تصحيح الأخطاء لإضافتها إلى جميع DomainObjects طريقة تعرض المحتويات الكاملة للكائن في إطار التصحيح. هذه الطريقة تسمى بطباعة. المشكلة هي أن عميل API الخاص بك قد يلاحظ أن كائن الفاتورة لديه أساليب Print() و Print(Integer) ، ويعتقد أن هذه هي الأحمال الزائدة ذات الصلة. ربما أول من يطبع نسخة واحدة فقط. ولكن هذا ليس على الإطلاق ما تصوره كمؤلف للفاتورة. ليس لديك أي فكرة عن DomainObject.Print . نعم ، هذا لا يعمل في VB. عند ظهور هذا الموقف ، يظهر تحذير ، ولكن الأهم من ذلك ، أن السلوك الافتراضي في VB هو الاختفاء حسب الاسم. بمعنى أنه حتى تشير صراحةً Overloads أن " Print عبارة عن حمل زائد Print فئة أساسية ، يتم إخفاء عضو من الفئة الأساسية (وأي حمولات زائدة منه) تمامًا. يتم عرض واجهة برمجة التطبيقات (API) التي أعلنت عنها في الأصل فقط لعملاء الصف. يعمل هذا افتراضيًا ، ولكن يمكنك القيام بذلك بشكل صريح من خلال الكلمة الأساسية Shadows. يمكن لـ C # القيام Overloads (على الرغم من أنه يأخذ في الاعتبار Shadows عندما تشير إلى مكتبة VB) ويفعل ذلك افتراضيًا (باستخدام الكلمة الأساسية new ). لكن هذا الاختلاف يبرز من وقت لآخر عندما تظهر بعض التسلسلات الهرمية في الميراث في المشروعات التي يتم تعريف فئة فيها بلغة وأخرى في لغة أخرى ، وهناك طرق محمّلة ، لكن هذا يتجاوز نطاق العنصر الحالي في قائمة الاختلافات.

7. VB11 وأدناه أكثر صرامة للأعضاء المحميين في الأدوية العامة


في الواقع ، قمنا بتغيير هذا بين VS2013 و VS2015. على وجه الخصوص ، قررنا عدم عناء إعادة التنفيذ. لكنني أكتب هذا التمييز في حالة استخدام الإصدار القديم ولاحظته. باختصار: إذا تم إعلان العضو المحمي بالنوع العام ، فيمكن للوريث ، كونه عامًا أيضًا ، الوصول إلى هذا العضو المحمي من خلال المثيل الموروث فقط باستخدام وسيطات النوع نفسها.

 Class Base(Of T) Protected x As T End Class Class Derived(Of T) Inherits Base(Of T) Public Sub F(y As Derived(Of String)) ' Error: Derived(Of T) cannot access Derived(Of String)'s ' protected members yx = "a" End Sub End Class 

gist.github.com/AnthonyDGreen/ce12ac986219eb51d6c85fa02c339a2f#file-protected-in-generics-vb

8. يقوم بناء الجملة "الوسيطة المسماة" في السمات دائمًا بتهيئة الخصائص / الحقول


يستخدم VB بناء الجملة نفسه := لتهيئة خصائص / حقول السمات مثل تمرير وسيطات الطريقة بالاسم. لذلك ، لا توجد طريقة لتمرير وسيطة إلى مُنشئ السمة بالاسم.

9. توجد جميع إعلانات المستوى الأعلى (عادة) ضمنياً في مساحة اسم الجذر للمشروع


يوجد هذا الاختلاف تقريبًا في فئة "الميزات المتقدمة" ، لكنني قمت بإدراجه في القائمة لأنه يغير معنى الرمز. يوجد حقل في خصائص مشروع VB:


بشكل افتراضي ، هذا هو ببساطة اسم مشروعك في وقت الإنشاء. هذا ليس هو نفس حقل "مساحة الاسم الافتراضية" في خصائص مشروع C #. تقوم مساحة الاسم الافتراضية ببساطة بتعيين الرمز الذي تتم إضافته افتراضيًا إلى الملفات الجديدة في C #. لكن مساحة الاسم الجذر في VB تعني أنه ، ما لم يذكر خلاف ذلك ، فإن كل إعلان من المستوى الأعلى في هذا المشروع يكون ضمنياً ضمن مساحة الاسم هذه. هذا هو السبب في أن قوالب مستندات VB لا تحتوي عادةً على أي إعلانات عن مساحة الاسم. علاوة على ذلك ، إذا قمت بإضافة تصريح مساحة اسم ، فإنه لا يتجاوز الجذر ، ولكن تتم إضافته إليه:

 Namespace Controllers ' Child namespace. End Namespace Namespace Global.Controllers ' Top-level namespace End Namespace 

gist.github.com/AnthonyDGreen/fd1e5e3a58aee862a5082e1d2b078084#file-root-namespace-vb

وبالتالي ، تعلن مساحة الاسم Controllers فعليًا عن مساحة اسم VBExamples.Controllers ، إلا إذا تخلصت من هذه الآلية من خلال إعلان Global بوضوح في مساحة الاسم.

هذا مناسب لأنه يوفر مستوى واحد من المسافة البادئة ومفهوم واحد إضافي لكل ملف VB. وهذا مفيد بشكل خاص إذا كنت تقوم بإنشاء تطبيق UWP (لأن كل شيء يجب أن يكون في مساحة الاسم في UWP) ، وهو مناسب للغاية إذا قررت تغيير مساحة اسم المستوى الأعلى لمشروعك بأكمله ، على سبيل المثال ، من بعض اسم الكود مثل Roslyn إلى إصدار أطول ، مثل Microsoft.CodeAnalysis ، نظرًا لأنك لست مضطرًا إلى تحديث كل ملف يدويًا في حل. من المهم أيضًا وضع ذلك في الاعتبار عند العمل مع مولدات الشفرة ومساحات أسماء XAML .vbproj ملف .vbproj الجديد.

10. لا يتم إنشاء الوحدات النمطية كصفوف تجريدية مختومة في IL ، بحيث لا تبدو تمامًا مثل فئات C # الثابتة والعكس صحيح.


كانت الوحدات النمطية في VB موجودة قبل فصول C # الثابتة ، على الرغم من أننا حاولنا في عام 2010 جعلها واحدة من حيث IL. لسوء الحظ ، كان هذا تغييرًا فاشلاً ، لأن XML Serializer (أو ربما كان ثنائيًا) لهذا الإصدار من .NET (أعتقد أنه تم إصلاحه) لم يرغب في إجراء تسلسل للنوع المتداخل في نوع لا يمكن إنشاؤه (وفئة مجردة لا يمكن). رمى استثناء.

لقد وجدنا هذا بعد إجراء التغييرات واستعادتها لأن بعض الكود استخدم في مكان ما نوع التعداد الذي تم تضمينه في الوحدة النمطية. ونظرًا لأنك لا تعرف أي إصدار من برنامج التسلسل الذي سيعمل معه البرنامج المترجم ، فلن يكون من الممكن أبدًا تغييره ، لأنه في إصدار واحد من التطبيق سيعمل ، وفي حالات أخرى ، سوف يلقي استثناءات.

11. لا تحتاج إلى طريقة واضحة لنقطة الإدخال (Sub Main) في تطبيقات WinForms


إذا كان مشروعك يستخدم النموذج ككائن بدء ولم يستخدم "Application Framework" (المزيد حول هذا الموضوع في المنشور التالي) ، فإن VB ينشئ Sub Main ، الذي ينشئ نموذج البدء الخاص بك ويمرره إلى Application.Run ، مما يوفر لك الملف بأكمله لإدارة هذه العملية ، إما طريقة إضافية في Form الخاص بك ، أو حتى الحاجة إلى التفكير في هذه المشكلة.

12. إذا قمت باستدعاء بعض أساليب وقت تشغيل VB المتقادمة (على سبيل المثال ، FileOpen) ، سيتم وضع علامة على طريقة الاستدعاء ضمنياً بخاصية لتعطيل المضمنة لأسباب تتعلق بالصحة


باختصار ، تعتمد أساليب العمل مع الملفات ذات النمط VB6 مثل FileOpen على سياقات خاصة بالتجميع حيث يوجد الكود. على سبيل المثال ، يمكن أن يكون الملف رقم 1 عبارة عن سجل في مشروع واحد وتهيئة في مشروع آخر. لتحديد التجميع الذي يعمل ، Assembly.GetCallingAssembly() استدعاء Assembly.GetCallingAssembly() . ولكن إذا قامت JIT بتضمين طريقتك في المتصل ، ثم من وجهة نظر المكدس ، لن يتم استدعاء أسلوب وقت تشغيل VB بواسطة طريقتك ، ولكن بواسطة المتصل ، والذي قد يكون في مجموعة مختلفة ، والتي يمكن أن تسمح بعد ذلك لرمزك بالوصول إلى الحالة الداخلية للمتصل أو انتهاكها كائن هذه ليست مشكلة أمان ، لأنه إذا كان كود التسوية قيد التشغيل في العملية الخاصة بك ، فقد فقدت بالفعل. هذه مسألة صحة. لذلك ، إذا كنت تستخدم هذه الأساليب ، يعطل المحول البرمجي inlining.

تم إجراء هذا التغيير في آخر لحظة في عام 2010 ، نظرًا لأن x64 JIT شديد العدوانية عند تضمين / تحسين الكود ، وقد وجدناه متأخراً للغاية ، وكان الخيار الأكثر أمانًا.

13. إذا تم تمييز النوع الخاص بك بالسمة DesignerGenerated ولم يحتوي على أي إعلامات صريحة للمنشئ ، فسوف يقوم المحول البرمجي الذي تم إنشاؤه افتراضيًا باستدعاء InitializeComponent إذا تم تعريفه لهذا النوع


في الفترة التي سبقت ظهور الأنواع الجزئية ، شن فريق VB حربًا لتقليل كود التعليمات البرمجية في مشاريع WinForms. ولكن حتى مع Partial يعد هذا مفيدًا لأنه يسمح للملف الذي تم إنشاؤه بحذف المنشئ تمامًا ، ويمكن للمستخدم أن يعلنه يدويًا في ملفه إذا لزم الأمر ، أو لا يعلنه إن لم يكن كذلك. بدون هذا ، سيضطر المصمم إلى إضافة مُنشئ فقط للاتصال بـ InitializeComponent ، وإذا أضاف المستخدم أيضًا ، فسيكون مكررًا ، أو يجب أن تكون مجموعة الأدوات ذكية بما يكفي لنقل المُنشئ من ملف المصمم إلى المستخدم وعدم إعادة إنشائه في المصمم إذا كان قد قام بالفعل موجود في ملف المستخدم.

14. عدم وجود المعدل الجزئي لا يعني أن النوع ليس جزئيًا


من الناحية الفنية ، في VB ، يجب تمييز فئة واحدة فقط كجزئية. هذا عادةً (في مشاريع واجهة المستخدم الرسومية) ملف تم إنشاؤه.

لماذا؟ هذا يبقي ملف المستخدم جميلًا ونظيفًا ، ويمكن أن يكون مناسبًا جدًا للتضمين بعد إنشاء أو إضافة رمز مخصص إلى الشفرة التي تم إنشاؤها. ومع ذلك ، يوصى بعدم وجود معدل جزئي على الأكثر في فصل واحد ، وإلا يتم إصدار تحذير.

15. في الفصول الافتراضية ، يكون مستوى الوصول العام لكل شيء باستثناء الحقول ، وفي الهياكل العامة ، للحقول أيضًا


لدي مشاعر مختلطة حول هذا الموضوع. في C # ، كل شيء private بشكل افتراضي (هتافات ، تغليف!) ، ولكن هناك حجة للقيام بها اعتمادًا على ما تعلنه غالبًا: عقد عام أو تفاصيل التنفيذ. الخصائص والأحداث مخصصة عمومًا للاستخدام الخارجي ( public ) ، ولا يمكن الوصول إلى المشغلين بخلاف public . ومع ذلك ، نادراً ما أعتمد على إمكانية الوصول الافتراضية (باستثناء العروض التوضيحية مثل الأمثلة الواردة في هذه المقالة).

16. يقوم VB بتهيئة الحقول بعد أن يسمى المنشئ الأساسي ، بينما C # يهيئها قبل أن يسمى المنشئ الأساسي


هل سمعت كيف يقول "البعض" أن أول شيء يحدث في المنشئ هو دعوة لمنشئ الطبقة الأساسية؟ حسنا ، هذا ليس هو الحال ، على الأقل في C #. في C # ، قبل استدعاء base() ، صريحًا أو ضمنيًا ، يتم تنفيذ مُهيئات الحقل أولاً ، ثم استدعاء المُنشئ ، ثم الكود. هذا القرار له عواقب ، وأعتقد أنني أعرف لماذا يمكن لمطوري اللغة أن يذهبون بطريقة أو بأخرى. أعتقد أن إحدى هذه العواقب هي أنه لا يمكن ترجمة التعليمات البرمجية التالية مباشرةً إلى C #:

 Imports System.Reflection Class ReflectionFoo Private StringType As Type = GetType(String) Private StringLengthProperty As PropertyInfo = StringType.GetProperty("Length") Private StringGetEnumeratorMethod As MethodInfo = StringType.GetMethod("GetEnumerator") Private StringEnumeratorType As Type = StringGetEnumeratorMethod.ReturnType Sub New() Console.WriteLine(StringType) End Sub End Class 

gist.github.com/AnthonyDGreen/37d01c8e7f085e06172bfaf6a1e567d4#file-field-init-me-reference-vb

في الأيام التي كنت أعمل فيها في برنامج Reflection ، كتبت غالبًا مثل هذا الرمز. وأذكر بشكل غامض زميلًا قبل Microsoft (Josh) ، قام بترجمة الكود الخاص بي إلى C # ، وأحيانًا يشكو من الحاجة إلى نقل جميع مُهيئاتي إلى المُنشئ. في C # ، يحظر الإشارة إلى كائن يتم إنشاؤه قبل استدعاء base() . ونظرًا لأن مُهيئات الحقل يتم تنفيذها قبل الاستدعاء المحدد ، فلا يمكنهم أيضًا الرجوع إلى الحقول الأخرى أو أي أعضاء في مثيل الكائن. لذلك هذا المثال يعمل أيضًا فقط في VB:

 MustInherit Class Base ' OOP OP? Private Cached As Object = DerivedFactory() Protected MustOverride Function DerivedFactory() As Object End Class Class Derived Inherits Base Protected Overrides Function DerivedFactory() As Object Return New Object() End Function End Class 

gist.github.com/AnthonyDGreen/fe5ca89e5a98efee97ffee93aa684e50#file-base-derived-init-vb

هنا لدينا فئة أساسية ، والتي من المفترض أن لديها الكثير من الوظائف ، لكنها تحتاج إلى بعض العناصر الرئيسية للإدارة ، العمل ، والتي يتم تحديدها حسب النوع المشتق. هناك العديد من الطرق لتطبيق مثل هذا القالب ، لكنني عادةً ما استخدم هذا القالب للأسباب التالية:

  • انه قصير
  • لا يطلب مني إعلان مُنشئ ؛
  • لا يطلب مني وضع رمز التهيئة في المنشئ ، إن وجد ؛
  • يسمح لي بتخزين الكائن الذي تم إنشاؤه مؤقتًا ولا يتطلب من الأنواع المشتقة إعلان وإدارة التخزين للعنصر المقدم ، على الرغم من أن هذه المشكلة ليست الآن مشكلة في الخصائص التلقائية.

علاوة على ذلك ، كنت في كلتا الحالتين: عندما يريد حقل من النوع المشتق استدعاء طريقة معلنة في الفئة الأساسية ، وعندما يكون مُهيئ الحقل للفئة الأساسية يحتاج إلى استدعاء عضو MustOverride تنفيذه بواسطة النوع المشتق. كلاهما صالح في VB وليس في C # ، وهذا منطقي. إذا كان باستطاعة مهيئ الحقل C # استدعاء عضو في الفئة الأساسية ، فقد يعتمد هذا العضو على الحقول التي تمت تهيئتها في المُنشئ الأساسي (وهو ليس قيد التشغيل بالفعل) وستكون النتائج غير صحيحة تقريبًا ، ولا توجد طريقة للتغلب على ذلك.

ولكن في VB ، فإن المنشئ الأساسي قد نجح بالفعل ، بحيث يمكنك القيام بأي شيء! في الموقف المعاكس ، يكون كل شيء أكثر تعقيدًا قليلاً ، لأن استدعاء العضو Overridable من Overridable (أو المُنشئ) للفئة الأساسية يمكن أن يؤدي إلى الوصول إلى الحقول قبل "تهيئة". ولكن التنفيذ الخاص بك فقط يعرف ما إذا كانت هذه مشكلة. في البرامج النصية الخاصة بي ، هذا لا يحدث. لا يعتمدون على حالة المثيل ، لكن لا يمكن أن يكونوا أعضاء Shared Overridable لأنه لا يمكن أن يكون لديك عضو Shared Overridable بأي لغة لأسباب فنية تتجاوز نطاق هذه المقالة. بالإضافة إلى ذلك ، يتم تعريف ما يحدث بشكل واضح للحقول قبل بدء مهيئات مخصصة - يتم تهيئتها بقيم افتراضية ، مثل كل المتغيرات في VB. لا مفاجآت.

فلماذا؟ في الواقع ، لا أعرف ما إذا كانت نصوصي هي ما كان يفكر فيه فريق VB.NET الأصلي عند تصميمه. انها تعمل فقط في حالتي! , : VB , , . . .

, , , C# VB, , VB , C#.

17. (backing field) VB , C#,


( ). E , VB ( IDE) EEvent . C# E , , E , .

18. VB


P , _P' . IntelliSense, . C# «» ( mangled ) , , C# .

? VB , -, «WithEvents», -, , - , .

19. read-only


, , …. VB « » . WithEvents -, non-Custom , , . IntelliSense, , , . FTW! , VB , private set; C#.

 Class Alarm Private ReadOnly Code As Integer ReadOnly Property Status As String = "Disarmed" Sub New(code As Integer) Me.Code = code End Sub Sub Arm() ' I'm motifying the value of this externally read-only property here. _Status = "Armed" End Sub Function Disarm(code As Integer) As Boolean If code = Me.Code Then ' And here. _Status = "Disarmed" Return True Else Return False End If End Function End Class 

gist.github.com/AnthonyDGreen/57ce7962700c5498894ad417296f9066#file-read-only-auto-property-backing-field-is-writeable-vb

20.


, NonSerialized .

VB (expanded) Custom- 2005 (?) , , , NonSerialized . , , , , «» , « ».

, , , , . , , , , two-way bindable ( , PropertyChanged ), , , , , .

, , CLSA «Expert Business Objects» (Rocky Lhotka) , undo/redo ( , , - , , ), . , . , , , .


21. — , ; ( )


, GoTo . , - . , For For Each ; Using , SyncLock With , , , Finally . If Select Case , Do While , Try — , :

 Module Program Sub Main() Dim retryCount = 0 Try Retry: ' IO call. Catch ex As IO.IOException When retryCount < 3 retryCount += 1 GoTo Retry End Try End Sub End Module 

gist.github.com/AnthonyDGreen/b93adcf3c3705e4768dcab0b05b187a0#file-try-goto-retry-vb

, , , .NET VB «». VB6 Quick Basic ( ) . QB, . , « », . GoTo, — , .

: Try , VB - await Catch Finally , , GoTo .

22. <>


, VB ( ) ( static ) ( ). , . Catch 3 . Try Catch , , , Try .

, VB.NET , . CLR VB . : , .

, C# , , «». VB.NET .

23.


, , C# « » ( definite assignment ). , , , « ». , ( ) , , . C/C++. , ! , . , , , — . , , , , , , , , . , BASIC , , «» , = Nothing , = 0 , = False ..

, ( flow analysis ) VB , .

, C# , , , . VB , , , . Roslyn, , API « », , .

24. RaiseEvent , null


, - C# VB. RaiseEvent VB — , null ( ), null - — , .

 ' You don't have to write this: If PropertyChangedEvent IsNot Nothing Then RaiseEvent PropertyChanged(Me, e) End If ' You don't have to write this: Dim handlers = PropertyChangedEvent If handlers IsNot Nothing Then handlers(Me, e) End If ' You don't have to write this either: PropertyChangedEvent?(Me, e) ' Just write this: RaiseEvent PropertyChanged(Me, e) 

gist.github.com/AnthonyDGreen/c3dea3d91ef4ffc50cfa92c41f967937#file-null-safe-event-raising-vb

, null-conditional C# VS2015 C# , VB ( ), , ; VB.NET .

25. ; (shallow clone)


, , 17 , , . (boxed) Object, System.Runtime.CompilerServices.RuntimeHelper.GetObjectValue . , CLR. , :

  • , .
  • , (, Integer ), .
  • , .

, , , , ( late-bound situations ). , , ( ) , , , , ( caller's copy ). , , - , — .

. :

 Class MyEventArgs Property Value As Object End Class Structure MyStruct Public X, Y As Integer End Structure Module Program Sub Main() Dim defaultValue As Object = New MyStruct With {.X = 3, .Y = 5} Dim e = New MyEventArgs With {.Value = defaultValue} RaiseEvent DoSomething(Nothing, e) If e.Value Is defaultValue Then ' No handlers have changed anything. Console.WriteLine("Unchanged.") End If End Sub Event DoSomething(sender As Object, e As MyEventArgs) End Module 

gist.github.com/AnthonyDGreen/422ac4574af92d9bbbf59f0fbc40b74d#file-get-object-value-vb

, WPF, . , . , , . , . , - , , , .

, , « » . IronRuby/Python, dynamic C# ( C#): C# GetObjectValue . object.ReferenceEquals , , , - dynamic C# ( ). == , . C#, , .

26. Select Case «» (fall-through); break


Friday , Sunday — , 5 .

 Module Program Sub Main() Select Case Today.DayOfWeek Case DayOfWeek.Monday: Case DayOfWeek.Tuesday: Case DayOfWeek.Wednesday: Case DayOfWeek.Thursday: Case DayOfWeek.Friday: Console.WriteLine("Weekday") Case DayOfWeek.Saturday: Case DayOfWeek.Sunday: Console.WriteLine("Weekend") End Select End Sub End Module 

gist.github.com/AnthonyDGreen/7b7e136c71dd11b2417a6c7267bb3546#file-select-case-no-fallthrough-vb

Roslyn C# , - : «, ? !» «, » . . VS , , , , , . !

. C# , C, C. . , C# , case . - , goto , break . VB break , Exit Select , , VB .

27. Case


, . C#, :

 Module Program Sub Main() Select Case Today.DayOfWeek Case DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday Dim message = "Get to work!" Case DayOfWeek.Saturday, DayOfWeek.Sunday Dim message = "Funtime!" End Select End Sub End Module 

gist.github.com/AnthonyDGreen/bd642061896246c9336255881fb78546#file-select-case-scopes-vb

, message , C# switch case — . . , , - ( , C): , , , , .

28, 29, 30. Select Case , =


, , , , Select Case .

, , . :

  • Select Case — , , …
  • switch — / , « ».

, 26-30. switch , , , , if . IL switch , , If , VB , . switch , , C . VB .

31. , ,


x , , -1, -2, -3:

 Module Program Sub Main() For i = 1 To 3 Dim x As Integer x -= 1 Console.WriteLine(x) Next End Sub End Module 

gist.github.com/AnthonyDGreen/cbc3a9c70677354973d64f1d993a3c5d#file-loop-variables-retain-their-values-vb

« , , » ( ). , VB2008 , -:

 Module Program Sub Main() Dim lambdas = New List(Of Action) For i = 1 To 3 Dim x As Integer x -= 1 lambdas.Add(Sub() Console.WriteLine(x)) Next For Each lambda In lambdas lambda() Next End Sub End Module 

gist.github.com/AnthonyDGreen/2ef9ba3dfcf9a1abe0e94b0cde12faf1#file-loop-variables-captured-per-iteration-vb

-1, -2, -3. x — « », - x , . , x . flow analysis API — ! ( «… … ?» )

? , , , , , #22. , , -, .

, VB C# ( control variables ) For Each VS2012 (?), - « ». 10000% , ( , VB , ). , VB For , . , . , VB For For Each , for foreach C#. , For VB - , , .

32. For


For . , , 1,3,5,7,9, , .

 Module Program Sub Main() Dim lower = 1, upper = 9, increment = 2 For i = lower To upper Step increment Console.WriteLine(i) upper += 1 increment -= 1 Next End Sub End Module 

gist.github.com/AnthonyDGreen/1e48113be204f515c51e221858666ac7#file-for-loop-bounds-cached-vb

, ( ), , , , IndexOutOfRangeExceptions , .

, , , , , C, VB . - , VB , For i = a To b Step c ( , i> b ) ( , i <b ), c ? , , , b , — . , , , , .

33. For Each VB GetEnumerator


For Each , IEnumerable , GetEnumerator , For Each .
, , For Each IEnumerator , , :

 Module Program Sub Main() Dim list = New List(Of Integer) From {1, 2, 3, 4, 5} Dim info = list.FirstAndRest() If info.First IsNot Nothing Then Console.Write(info.First.GetValueOrDefault()) For Each other In info.Additional Console.Write(", ") Console.Write(other) Next Console.WriteLine() End If End Sub <Runtime.CompilerServices.Extension> Function FirstAndRest(Of T As Structure)(sequence As IEnumerable(Of T)) As (First As T?, Additional As IEnumerator(Of T)) Dim enumerator = sequence.GetEnumerator() If enumerator.MoveNext() Then Return (enumerator.Current, enumerator) Else Return (Nothing, enumerator) End If End Function <Runtime.CompilerServices.Extension> Function GetEnumerator(Of T)(enumerator As IEnumerator(Of T)) As IEnumerator(Of T) Return enumerator End Function End Module 

gist.github.com/AnthonyDGreen/d7dbb7a5b98a940765c4adc33e3eaeee#file-for-each-extension-get-enumerator-vb

F# , IEnumerator , For Each , .

VB , ( well-known name ), . , , Add, . C# , (. async / await ). , C# Roslyn () , .
دقيقة من الإعلانات. 15-16 - .NET- DotNext 2019 Piter . , . , . .

Source: https://habr.com/ru/post/ar443684/


All Articles