c# - لا عامل زيادة في VB.net



operators (5)

كما قالpaxdiablo ، في VB (أو بالأحرى ، في سلفه BASIC) ، كل شيء اعتاد أن يكون بيانًا . وفي الواقع ، تم تقديم كل عبارة بكلمة رئيسية.

لذلك لتخصيص متغير كان لدينا

LET x = x + 1

وللاستدعاء طريقة ، كان لدينا

CALL SomeMethod

في VB ، تم إسقاط LET و CALL أخيراً (ما عدا في ظرف خاص واحد) لأنه زائد بالكامل ولا يضيف الوضوح. لكن القواعد اللغوية المعنوية لـ VB لم تغير كل هذا القدر: كل عبارة ما زالت يجب أن تكون بيانًا . i++ ليست عبارة في VB ، لأنها تفتقر إلى استدعاء دالة أو مهمة.

كان هناك حجة في النسخة الأولى من VB.NET سواء لإدخال مشغلي ما قبل وبعد الزيادة كما هو الحال في C #. تقرر عدم القيام بذلك ، لسبب بسيط إلى حد ما: استخدام الآثار الجانبية في التعبيرات غير مستحسن على أي حال. وعادة ما يسمح للوضوح يعاني. إذن ، حتى في الاستخدامات الشرعية لـ C i++ لـ i++ في تعبير نادرة للغاية ، والاستخدامات الشرعية لـ ++i أكثر ندرة (على الرغم من أنني لن أنكر أنه في بعض الحالات يضيف الوضوح).

في معظم الحالات ، يمكنك استخدام i += 1 ما يرام وهذا يعبر عن النية بشكل جيد.

لاحظ أنه في C ++ ، يختلف الوضع اختلافًا جوهريًا لأن هنا (ولكن ليس في C #!) i++ له في الواقع دلالات مختلفة عن i += 1 بسبب التحميل الزائد من المشغل (في C # لدينا أيضًا التحميل الزائد على المشغل ولكن ++ لا يمكن تحميله بشكل زائد).

أنا جديدة إلى حد ما vb.net وواجهت هذه المسألة أثناء تحويل حلقة for في C # إلى VB.net أدركت أن مشغلي الزيادة غير متوفرة في vb.net (++ و -) بينما كنت قادراً على القيام بذلك شيء مثل cnt +=1

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

آمل أن يتمكن أحد الأشخاص هنا من شرح سبب عدم نجاح ذلك بالطريقة نفسها التي يعمل بها في C #. (آمل أن يكون هذا صحيحا أيضا كما هو الحال في لماذا لدينا == في C # للمقارنة)


كمثال على الفرق بين التعبير والعبارة في VB ، في VB ، ينتج عن ذلك خطأ في المحول البرمجي حيث أن count += 1 تزاد count 1 ، لكن count += 1 التعبير الكامل count += 1 لا يُرجع نتيجة ، لذلك لا يمكن يمكن استخدامها كمعلمة.

Dim count As Integer = 0
Console.WriteLine(count += 1)  ' compiler error

عليك القيام بذلك بدلا من ذلك

Dim count As Integer = 0
count += 1
Console.Writeline(count)

بالطبع ينطبق نفس الشيء على استخدام += عامل في سلسلة.

ماذا يعني "في VB ، العبارة لا يمكن أن يكون مجرد تعبير" يعني؟

  • يتطلب برنامج التحويل البرمجي VB أن يتم استهلاك النتائج في بعض المهام أو العمليات الأخرى.
  • وبسبب هذا ، لا ينتج عن عملية التعيين في VB نتيجة. إذا كان مترجم VB لا يسمح له بالوقوف بمفرده كبيان (يتطلب المحول أن يتم استهلاك النتائج).
  • وبالتالي يمكن استخدام التعيينات في VB كبيانات ، ولكن ليس كتعبيرات. هذا لا يمكنك استخدام عبارة الواجب كمعلمة إلى أسلوب أو نتيجة متوسطة.
  • في C # تقوم عملية تخصيص بإعطاء قيمة. وبالتالي من أجل أن تكون التعيينات قائمة بذاتها كبيانات ، لا يحتاج المترجم إلى استهلاك جميع النتائج.
  • النتيجة الطبيعية في C # هي أن أي عملية أخرى تنتج نتيجة يمكن أن تقف وحدها كبيان. 2 + 2 على سبيل المثال ينتج النتيجة 4 ويمكن أن يقف وحده كبيان ، بينما في VB لا يمكن.

إجابة مبسطة على "لماذا لا تتوفر عوامل التشغيل قبل وبعد الزيادة في VB؟"

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

يقول ++count ، أول count الزيادة ، ثم إرجاع قيمة الواجب count .
في هذه الحالة ، يتم إرجاع قيمة تعيين +1 إلى count كقيمة التعبير. كما ذكر من قبل ، لا تنتج المهام في VB نتيجة.
وبالتالي سيكون هناك بعض الألم الشديد في تنفيذ هذه المشغلين في VB.


أساليب ملحق التالية نسخ متماثل ++x x++ --x

Public Module INC_DEC

  <Runtime.CompilerServices.Extension>
  Public Function PreINC(ByRef x As Integer) As Integer
    Return Interlocked.Increment(x)
  End Function

  <Runtime.CompilerServices.Extension>
  Public Function PostINC(ByRef x As Integer) As Integer
    Dim tmp = x
    Interlocked.Increment(x)
    Return tmp
  End Function

  <Runtime.CompilerServices.Extension>
  Public Function PreDEC(ByRef x As Integer) As Integer
    Return Interlocked.Decrement(x)
  End Function

  <Runtime.CompilerServices.Extension>
  Public Function PostDEC(ByRef x As Integer) As Integer
    Dim tmp = x
    Interlocked.Decrement(x)
    Return tmp 
  End Function
End Module

أود أن أقول إن مصممي اللغة ببساطة يعتقدون أن BASIC كان خط الأساس أفضل من C عند تصميم Visual BASIC . يمكنك اتباع نسب C (و ، BCPL سابقًا) من خلال C++ و Java و C# .

النسب VB يأتي من BASIC الأصلي من دارتموث (و ، في وقت سابق ، Fortran ) وحش مختلف تماما.

بعبارة أخرى ، ما بدأ باسم BASIC الموقر:

LET I = I + 1

ربما تم اختراق ودمر ما يكفي :-)

حسب مشاركة Eric ، i++; هو في الواقع مجرد تعبير ، الذي ينتج عنه التأثير الجانبي الذي يتم زيادته بعد الحدث ( i++; هو تعبير ، تمامًا مثل التعبير غير الجانبي - تأثير i; ).

هذا لأن C تسمح هذه التعبيرات المجردة ، حتى الأشياء مثل 42; التي لا تفعل الكثير ولكنها صالحة تمامًا. بمعنى آخر ، ما يلي هو برنامج C كامل:

int main (void) { 1; 2; 3; 4; 5; 6; 7; 8; 9; return 0; }

كل هذه التعبيرات صالحة لكنها غير مجدية.

في BASIC ، لم يتم ذلك فعلاً ، لأن BASIC يتألف من عبارات (أشياء فعلت شيئًا ما). هذا هو السبب في أن i += 1 (عبارة متزايدة i ) تعتبر كوشير لكن i++ (تعبير لا يفعل أي شيء يحدث فقط أن يكون له تأثير جانبي والذي يزيد i ) ليس كذلك. يمكنك أن تجادل أنه مجرد تقسيم شعري دلالات ولا شك في أن المصممين VB لم يجادل.

لكن المجموعة التي فازت في اليوم كانت "نحن لا نحتاج إلى لغة ستينكين C في لغتنا الحبيبة".

يجب أن تكون شاكراً للمُرحمات الصغيرة ، على الأقل أنت لست مضطراً للتعامل مع كوبول:

ADD 1 TO DD_WS_I.

لنفترض أن لدينا بايت واحد:

0110110

تطبيقنا على bitshift الأيسر واحد يحصل لنا:

1101100

تم نقل الصفر الموجود أقصى اليسار من البايت ، وتم إلحاق الصفر الجديد بالنهاية اليمنى للبايت.

لا تمرر البتات ؛ يتم التخلص منها. وهذا يعني أنك إذا تركت shift 1101100 ومن ثم قمت بتحويله إلى اليمين ، فلن تحصل على نفس النتيجة مرة أخرى.

يكافئ التحويل إلى اليسار بواسطة N ضرب بـ 2 N.

يعد التبديل إلى اليمين بـ N (إذا كنت تستخدم مكملًا واحدًا ) هو ما يعادل القسمة على 2 N وتقريبه إلى الصفر.

يمكن استخدام Bitshifting للتكاثر والقسمة بشكل سريع بجنون ، بشرط أن تعمل مع طاقة 2. جميع إجراءات الرسومات منخفضة المستوى تقريبًا تستخدم bitshifting.

على سبيل المثال ، في طريق العودة في الأيام القديمة ، استخدمنا الوضع 13h (ألوان 320x200 256) للألعاب. في الوضع 13h ، تم وضع ذاكرة الفيديو بالتسلسل لكل بكسل. هذا يعني حساب موقع بكسل ، يمكنك استخدام الرياضيات التالية:

memoryOffset = (row * 320) + column

الآن ، مرة أخرى في ذلك اليوم وهذا العصر ، كانت السرعة حرجة ، لذلك كنا نستخدم bitshifts للقيام بهذه العملية.

ومع ذلك ، 320 ليست قوة من اثنين ، لذلك للحصول على هذا يجب علينا أن نعرف ما هي قوة اثنين التي تضاف معا يجعل 320:

(row * 320) = (row * 256) + (row * 64)

الآن يمكننا تحويل ذلك إلى نوبات اليسار:

(row * 320) = (row << 8) + (row << 6)

للحصول على نتيجة نهائية لـ:

memoryOffset = ((row << 8) + (row << 6)) + column

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

mov ax, 320; 2 cycles
mul word [row]; 22 CPU Cycles
mov di,ax; 2 cycles
add di, [column]; 2 cycles
; di = [row]*320 + [column]

; 16-bit addressing mode limitations:
; [di] is a valid addressing mode, but [ax] isn't, otherwise we could skip the last mov

المجموع: 28 دورة على أي وحدة المعالجة المركزية القديمة كان هذه التوقيتات.

جيش صرب البوسنة

mov ax, [row]; 2 cycles
mov di, ax; 2
shl ax, 6;  2
shl di, 8;  2
add di, ax; 2    (320 = 256+64)
add di, [column]; 2
; di = [row]*(256+64) + [column]

12 دورة على نفس وحدة المعالجة المركزية القديمة.

نعم ، سوف نعمل جاهدين على حلق 16 دورة CPU.

في وضع 32 أو 64 بت ، تحصل كلا الإصدارين على أقصر وأسرع. تحتوي وحدات المعالجة المركزية (CPU) غير المصممة للتنفيذ الحديثة مثل Intel Skylake (انظر http://agner.org/optimize/ ) على أجهزة سريعة جدًا تتضاعف (زمن انتقال منخفض ومعدل نقل عالي) ، وبالتالي يكون الكسب أصغر كثيرًا. إن عائلة البلدوزر AMD أبطأ قليلاً ، خاصة بالنسبة إلى 64 بت مضاعفة. في معالجات إنتل ، و AMD Ryzen ، فإن نوبتين هما زمن انتقال أقل بقليل ولكن تعليمات أكثر من مضاعفة (مما قد يؤدي إلى انخفاض الإنتاجية):

imul edi, [row], 320    ; 3 cycle latency from [row] being ready
add  edi, [column]      ; 1 cycle latency (from [column] and edi being ready).
; edi = [row]*(256+64) + [column],  in 4 cycles from [row] being ready.

ضد.

mov edi, [row]
shl edi, 6               ; row*64.   1 cycle latency
lea edi, [edi + edi*4]   ; row*(64 + 64*4).  1 cycle latency
add edi, [column]        ; 1 cycle latency from edi and [column] both being ready
; edi = [row]*(256+64) + [column],  in 3 cycles from [row] being ready.

سيقوم المترجمون بذلك نيابة عنك: انظر كيف تستخدم gcc و clang و MSVC shift + lea عند تحسين return 320*row + col; .

إن الشيء الأكثر إثارة للاهتمام هنا هو أن x86 يحتوي على تعليمة shift-and-add ( LEA ) التي يمكن أن تقوم بتغييرات صغيرة في اليسار وتضيفها في نفس الوقت ، مع الأداء add تعليمات. ARM هو أكثر قوة: يمكن ترك المعامل واحد من أي تعليمات أو اليمين تحول مجانا. لذلك يمكن أن يكون التدرج بواسطة ثابت وقت التجميع المعروف بكونه طاقة لـ 2 أكثر كفاءة من التكاثر.

موافق ، مرة أخرى في الأيام الحديثة ... شيء أكثر فائدة الآن سيكون استخدام bitshifting لتخزين قيمتين 8 بت في عدد صحيح 16 بت. على سبيل المثال ، في C #:

// Byte1: 11110000
// Byte2: 00001111

Int16 value = ((byte)(Byte1 >> 8) | Byte2));

// value = 000011111110000;

في C ++ ، يجب على compilers القيام بذلك نيابة عنك إذا استخدمت struct ذات عضوين 8 بت ، ولكن من الناحية العملية لا دائمًا.





c# vb.net operators