المتابعون للمدونة

الخميس، 21 مارس 2019

الغزال الأسمر




VB.NET Razor


مفاجأة مدهشة: أنشأت تطبيق ASP.NET MVC Core بلغة فيجوال بيزيك وعمل بشكل صحيح!
 

تعرفون بالطبع أنني سقطت في عشق لغة فيجوال بيزيك من أول نظرة عام 1998 وما زلت مغرما بها حتى اليوم، وأرى أنها أرقى ما وصلت إليه لغات البرمجة من حيث صياغتها القريبة من اللغة الإنجليزية العادية، والتسهيلات الذكية الكثيرة التي تمنحها للمبرمج فتجعله يكتب الكود ويحصل على النتائج الصحيحة بسرعة.. كل هذا جعلني مستاءا للغاية من إهمال ميكروسوفت لفيجوال بيزيك وتركها حبيس الويندوز وعدم استخدامها في Xamarin و ASP.NET Core.. وكلما سألهم سائل عن ذلك، تكون الإجابة إنك تستطيع استخدام فيجوال بيزيك بالفعل في هذه المنتجات، لكن في مكتبات الكود وليس تصميم الواجهات، وإن دعم ملفات vbhtml في تقنية Razor أمر يحتاج وقتا وجهدا وتكلفة عالية، وهم مشغولون في أشياء كثيرة أولى!
وقد ظللت أنتظر عامين لعل هذه الساسة تتغير في أي لحظة، لكن بلا جدوى!.. لهذا بدأت أميل إلى الاقتناع إلى أن ميكروسوفت ستهمل فيجوال بيزيك بالتدريج وتسقطها من حساباتها إلى أن تموت موتا بطيئا مؤلما!
وقد قررت ألا أجعل هذا الأمر سهلا بالنسبة لهم، لهذا عرضت عليهم الأسبوع الماضي أن أتطوع لإنشاء VB.NET Razor حتى لو استغرق هذا ستة أشهر، وأن يعوني بعض التوجيهات لإنجاز المهام المطلوبة، لكن ردهم جاء كما توقعت برفض الفكرة، بحجة أن دعم وتطوير VB Razor بعد أن أكتبه لهم مكلف في حد ذاته وليس لديهم استعداد لهذا!!
في هذه اللحظة قررت أن أمضي قدما بمفردي، ولأن الموضوع معقد فعلا، قررت تبسيطه بأن أفصل كود فيجوال بيزيك في ملف خلفي code-behind file مثلما يحدث في WPF، وأن يقتصر ملف vbhtml على كود HTML مع وجود إشارات موضعية لخصائص معرفة في ملف الكود الخلفي يتم التعويض عنها بقيمها مباشرة.
وبتأمل هذه الفكرة، تذكرت فجأة أن فيجوال بيزيك تدعم كتابة كود XML مباشرة في الكود مثل:
Dim X = <p>sample</p>
مع توفير التنسيق والتلوين وتدقيق الصياغة لهذه التعبيرات!.. والأجمل من هذا أنني أستطيع التعويض بقيم المتغيرات داخل هذه الصيغ، بوضعها داهل القوسين المستخدمين في ASP على الصيغة <%=  %>.. مثل:


الأحد، 17 مارس 2019

أحببتك أنت




معامل معالجة العدم Null-Coalesce Operator


معامل معالجة العدم Null-Coalesce Operator:
تمتلك كل من فيجوال بيزيك وسي شارب معامل معالجة العدم Null-Coalesce Operator، وهو يستقبل متغيرين، حيث يعيد قيمة المتغير الأول لو كانت لا تساوي null (أو Nothing في فيجوال بيزيك)، وإلا فإنه يعيد قيمة المتغير الثاني.
وتمتاز هذه الصيغة في سي شارب بأنها مختصرة وتسمح بالاختيار من سلسلة طويلة من البدائل ببساطة:
var r = a ?? b ?? c ?? d ?? e;
العبارة (a ?? b ?? c ?? d ?? e) ستعيد قيمة أول متغير غير منعدم (قيمته لا تساوي null)، فإذا كانت كل المتغيرات منعدمة، فستعيد null.
للأسف فعل نفس الشيء في فيجوال بيزيك ليس بنفس البساطة، لأنها تستخدم if() لفحص قيمة متغيرين فقط، ولو أردت تنفيذ الكود السابق فستحتاج إلى استخدام عدة جمل If متداخلة كالتالي:
Dim r = If(a, If(b, If(c, If(d, e))))
قد لا يبدو لك الأمر هاما، لكن واقعيا، a, b , c, d, f تكون عبارات برمجية طويلة وليست مجرد أسماء مختصرة كهذه.. مثلا:
C#:
var t = CheckToolStrip(c as ToolStrip) ??
            CheckToolStrip(c.ContextMenuStrip) ??
            CheckControls(c);
 
VB.NET:
Dim t = If(CheckToolStrip(TryCast(c, ToolStrip)),
                  If(CheckToolStrip(c.ContextMenuStrip),
                       CheckControls(c)))
لو حاولت أن تضيف قيمة رابعة للكود السابق في سي شارب، فيكفي أن تضيف العلامة ؟؟ في السطر الأخير ثم تكتب العبارة الجديدة.. أما في فيجوال بيزيك، فعليك أن تضيف جملة If جديدة في البداية، وتجعل معاملها الأول هو العبارة الجديد، ومعاملها الثاني هو كل الكود السابق!
لحل هذه المشكلة، كتبت هذه الدالة:
Function Coalesce(Of T)(ParamArray expressions() As T) As T
        For Each expr In expressions
            If expr IsNot Nothing Then Return expr
        Next
End Function
وبهذا يمكن استخدامها كالتالي:
dim x = Coalesce(a, b, c, d, e)
هذا الكود أوضح وأسهل، لكن فيه مشكلة تجعله أقل كفاءة، وهي أن فيجوال بيزيك ستحسب قيم كل العبارات لإرسالها إلى معاملات الدالة Coalesce.. هذه العبارات قد تكون دوالا كما في هذا المثال:
Dim t = Coalesce(CheckToolStrip(TryCast(c, ToolStrip)),
              CheckToolStrip(c.ContextMenuStrip),
              CheckControls(c))
استخدام الدالة Coalesce في الكود السابق سيضمن استدعاء الدوال الثلاث حتى لو كانت الدالة الأولى فقط هي التي ستعيد قيمتها.. وفي المشروع الذي كتبت فيه هذا الكود، هذه الدوال دوال ارتدادية Recursive Functions، أي أنها تظل تستدعي نفسها إلى أن تصل إلى نهاية شجرة العناصر، وهذا معناه أن تنفيذ دالة أو دالتين بدون داعي سيستهلك وقتا ملموسا!
هذا لا يحدث عند استخدام If() لأنها تتجاهل تنفيذ أي عبارات لا لزوم لها (يسمى هذا Short-Circuiting  وهو تعبير مأخوذ من مصطلح جعل الدائرة الكهربية قصيرة، أو بالتعبير الشائع: عمل قفلة).
لحل هذه المشكلة، يمكن اللجوء إلى حيلة صغيرة، وهي ألا نرسل العبارات مباشرة، وإنما نضع كل عبارة في دالة بدون معاملات كل مهمتها أن تحسب قيمة العبارة وتعيدها.. ونطور الدالة Coalesce لنجعلها تستقبل دالة العبارات بدلا من العبارات نفسها كالتالي:
Function Coalesce(Of T)(ParamArray expressions( ) As Func(Of T)) As T
        For Each exp In expressions
            Try
                Dim v = exp( )
                If v IsNot Nothing Then Return v
            Catch
            End Try
        Next
        Return Nothing
End Function
لاحظ أنني طورت كود الدالة بحيث تعالج أي أخطاء تحدث عند استدعاء دالة حساب قيمة العبارة، وهذا معناه أن الدالة Coalesce ستتجاهل القيم المنعدمة أو التي حدث خطأ عند حسابها.
الآن، وباستخدام الدوال الفورية، صار من السهل استخدام هذه الدالة كالتالي:
Dim t = Coalesce(
              Function( ) CheckToolStrip(TryCast(c, ToolStrip)),
              Function( ) CheckToolStrip(c.ContextMenuStrip),
              Function( ) CheckControls(c))
هذا الكود سيرسل عناوين الدوال إلى الدالة Coalesce، وهي التي ستقوم باستدعاء كل دالة واحدة تلو أخرى، إلى أن تجد قيمة غير منعدمة فتعيدها وتتوقف عن استدعاء باقي الدوال.. وهذا هو الأداء المطلوب.
قد يبدو هذا الكود أطول قليلا من استخدام if ولكنه أكثر وضوحا بسبب عدم تداخل جمل if وأقواسها الكثيرة.
وقد اقترحت أن تضيف فيجوال بيزيك الجملة Coalesce لتعمل بآلية short-circuiting مباشرة بدون أن نضطر نحن إلى استخدام الدوال الفورية، وأتمنى أن يستجيبوا لهذا المقترح.
 


الأربعاء، 13 مارس 2019

نوتيلا




نطاق المتغيرات الموضعية


نطاق المتغيرات الموضعية
 
تعرفون أن المتغيرات المحلية Local Variables في فيجوال بيزيك وسي شارب تكون مرئية في النطاق الذي توجد فيه.. فيمكنك مثلا تعريف متغير داخل جملة شرط، ولن تستطيع استخدامه خارجها:
C#
var x = 2;
if (x > 1)
{
    var msg = "x > 2";
    Console.WriteLine(msg);
}
else
{
    var msg = "x <= 2";
    Console.WriteLine(msg);
}
تلاحظ أنك استطعت تعريف متغيرين كل منهما اسمه msg لأن كلا منهما مرئي فقط في المقطع الصغير الذي تم تعريفه فيه.
لكنك لو حاولت تعريف متغير اسمه msg بعد نهاية الجملة if..else فستحصل على خطأ لأن المتغير الثالث مرئي داخل جملة if..else لأنه معرف في المقطع الخارجي الذي يحتوي على if..else.
VB:
Dim x = 2
If x > 1 Then
    Dim msg = "x > 2" ' خطأ
    Console.WriteLine(msg)
else
    Dim msg = "x <= 2" ' خطأ
    Console.WriteLine(msg)
End If
Dim msg = "done"
Console.WriteLine(msg)
 
 قد يجعلك هذا تتعجب، لأن المتغير الثالث سيتم تعريفه بعد انتهاء جملة الشرط، وبالتالي لن يؤثر عليها.. لكن ليس هكذا تعمل لغة البرمجة.. على سبيل المثال: لو وضعت كل هذا الكود في حلقة دوران مثل Do Loop فهذا معناه أن التنفيذ سيعود لأعلى مرة أخرى وسيختبر جملة الشرط مرة أخرى، وسيحدث ارتباك بين قيمتي المتغيرين اللذين لهما نفس لاسم!
يمكنك حل هذه المشكلة في سي شارب بتعريف نطاق خاص بالمتغير الثالث، بوضعه في القوسين {}:
C#
var x = 2;
if (x > 1)
{
    var msg = "x > 2";
    Console.WriteLine(msg);
}
else
{
    var msg = "x <= 2";
    Console.WriteLine(msg);
}
 
{
    var msg = "done";
    Console.WriteLine(msg);
}
 
لا يوجد شيء مثل هذا في فيجوال بيزيك، لكن يمكن الحصول عليه بحيلة صغيرة، مثل وضع المتغير الثالث في جملة شرط صحيحة دائما:
Dim x = 2
If x > 1 Then
    Dim msg = "x > 2" ' خطأ
    Console.WriteLine(msg)
else
    Dim msg = "x <= 2" ' خطأ
    Console.WriteLine(msg)
End If
 
If True Then
   Dim msg = "done"
   Console.WriteLine(msg)
End If
أو وضعه في المقطع With بالحيلة التالية:
Witn Nothing
   Dim msg = "done"
   Console.WriteLine(msg)
End With
 


الأحد، 17 فبراير 2019

كوني أنت




* الجِنانُ: الجنّات.
* الجَناةُ: الثمرة المقطوفة، والجمع جَنَى (أي ما تجنيه من الثمار).
* أَلْفَيتُ: وجدتُ.


قاعدة تقريب رجال المصارف!!



قاعدة تقريب رجال المصارف!!

ذكرت في منشور سابق إن أحد الفروق بين فيجوال بيزيك وسي شارب، هو أن تحويل عدد به كسر عشري إلى عدد صحيح يتم في فيجوال بيزيك بالتقريب (تحويل 3.8 إلى عدد صحيح يعطي 4) بينما يتم في سي شارب بالتخلص من الكسر العشري بدون تقريب الجزء الصحيح (تحويل 3.8 إلى عدد صحيح يعطي 3)!

المفاجأة التي اكتشفتها الآن بعد 21 سنة من التعامل مع فيجوال بيزيك، هي الطريقة الغريبة التي تقرب بها فيجوال بيزيك الكسر 0.5 ، فهي تقربه إلى أقرب عدد زوجي!!.. فمثلا العدد 3.5 سيتم تقريبه إلى 4، والعدد 4.5 سيتم تقريبه إلى 4 أيضا وليس 5!!.. هذه القاعدة في تقريب النصف تسمى قاعدة تقريب رجال المصارف Bankers rounding!.. الحقيقة أنني لم أمر بأي موقف يكشف هذا التصرف الغريب من قبل، وأشك أنه لم يكن موجودا في VB6 وربما كذلك في الإصدارات الأولى من VB.NET!.. لاحظ أن هذه هي الطريقة التي تعمل بها أيضا الدالة Math.Round ما لم تستخدم معاملها الأخير لاختيار تقريب نقطة المنتصف إلى العدد الأبعد عن الصفر (سالبا أو موجبا).
جرب:
VB.NET:
Console.WriteLine(CInt(4.5)) ' 4
Console.WriteLine(CInt(3.5)) '  4 

C#:
Console.WriteLine((int)4.5); // 4
Console.WriteLine((int)3.5); // 3

 

 


الأربعاء، 13 فبراير 2019

مش مسامحك




كتب برمجة مجانية



كتب برمجة للتحميل مجانا، بقلم م. محمد حمدي غانم:

1- المبرمج الصغير: الجزء الأول، الجزء الثاني.




5- من الصفر إلى الاحتراف: برمجة قواعد البيانات في سي شارب:


الجمعة، 1 فبراير 2019

رجل ذكوري




DataTableReader Class



فئة قارئ جدول البيانات DataTableReader Class 

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

ولإنشاء قارئ بيانات يقرأ سجلات أحد الجداول، عليك باستخدام الوسيلة CreateDataReader الخاصة بهذا الجدول كالتالي:
var Tr = Ds.Tables["Authors"].CreateDataReader( );
while (Tr.Read( )) {
     MessageBox.Show(Tr["ID"].ToString( ));
     MessageBox.Show(Tr["Author"].ToString( ));
}
ولإنشاء قارئ بيانات يقرأ سجلات كل الجداول، عليك باستخدام الوسيلة CreateDataReader الخاصة بمجموعة البيانات DataSet كالتالي:
DataTableReader Tr = Ds.CreateDataReader( );
do {
   while (Tr.Read( )) {
          string RowTxt = "";
          for (var I = 0; I < Tr.FieldCount; I++)
                  RowTxt += Tr.GetName(I) + " = "
                         + Tr[I].ToString( ) + "\r\n";
            MessageBox.Show(RowTxt);
   }
} while (Tr.NextResult( ));
لاحظ أننا لم نستخدم أسماء الأعمدة عند قراءة خانات كل سجل، وذلك لأن السجلات ستختلف من جدول إلى آخر في عدد الأعمدة وأسمائها.. وبدلا من هذا استخدمنا الخاصية FieldCount لإنشاء حلقة تكرار تمر عبر كل الأعمدة، لقراءة كل خانة باستخدام رقم العمود بدلا من اسمه.
ويمكنك تجربة هذا الكود في المشروع DataTableReaderSample.
ولحدث إنشاء الفئة DataTableReader الصيغتان التاليتين:
الأولى تستقبل كائن الجدول DataTable الذي ستقرأ سجلاته.
والثانية تستقبل مصفوفة جداول DataTable Array لتقرأ سجلاتها.
ولا تمتلك هذه الفئة أية خصائص أو وسائل جديدة غير ما ترثه من الفئة الأم.

من كتاب من الصفر إلى الاحتراف: برمجة قواعد البيانات بتقنية ADO.NET
 


العيدية




جافا سكربت



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

لهذا أنهيت شرح لغة جافا سكربت في كتاب HTML5 بسرعة، لكني غارق منذ أكثر من شهرين في شرح DOM (Document Object Model) أي الكائنات التي تمثل الصفحة وكل عناصر HTML التي توضع فيها للتعامل معها من كود جافا سكربت!.. ورغم أن الفئات ووسائلها تشبه إطار العمل بل موجودة في دوت نت في الأداة WebBrowser Control (والتي أفكر أن أشرحها بالمرة في ملحق في نهاية هذا الكتاب حتى لا أعيد شرح DOM في كتاب آخر)، فإن هناك بعض الاختلافات المستفزة، فجافا سكربت مثلا لا تحتوي على المرقمات Enums ولهذا تعيد بعض الخصائص أرقاما، بينما تعيد بعضها الأخرى قيما نصية، والأعجب أن بعض الخصائص تعيد قيمة منطقية bool والبعض الآخر يعيد نصا قيمته 'true' أو 'false' مما يكاد يصيبني بالجنون!!

هناك الكثير من عدم التناسق في الواجهة البرمجية لجافا سكربت، لأنها تشكلت عبر سنوات طويلة عبر شركات مختلفة إلى أن استقروا على المقاييس الموحدة الحالية!

ربنا يسهل وأخرج من هذه الدوامة قريبا!

 


الثلاثاء، 29 يناير 2019

شاب الصبي




DataRow Class Methods


وسائل فئة صف البيانات DataRow Class:
 
له نسخة HasVersion:
تعيد true، إذا كان السجل الحالي يمتلك نسخة البيانات الموضحة في المعامل المرسل إلي هذه الوسيلة، وهو من نوع المرقم DataRowVersion الذي تعرفنا عليه من قبل.
والمثال التالي يعرض قيمة الخانة الأولى في نسخة السجل المقترحة إن وجدت:
if (Row.HasVersion(DataRowVersion.Proposed))
    MessageBox.Show(Row[0,
                  DataRowVersion.Proposed].ToString( ));
 
تغيير الحالة إلى مضاف SetAdded:
تغير قيمة الخاصية RowState إلى Addded.. وتسبب هذه الوسيلة خطأ في البرنامج إذا كان كانت حالة السجل تشير إلى أنه معدل.. ولحل هذه المشكلة، عليك استدعاء الوسيلة AcceptChanges أولا.
 
تغيير الحالة إلى معدل SetModified:
تغير قيمة الخاصية RowState إلى Modified.. وتسبب هذه الوسيلة خطأ في البرنامج إذا كان كانت حالة السجل تشير إلى أنه معدل.. ولحل هذه المشكلة، عليك استدعاء الوسيلة AcceptChanges أولا.
 
قبول التغييرات AcceptChanges:
يؤدّي استدعاء هذه الوسيلة إلى قبول التغييرات التي حدثت على الصف الحالي منذ أن تمّ تحميله من قاعدة البيانات، أو منذ آخر مرّة تمّ فيها استدعاء الوسيلة AcceptChanges.. ليس معنى هذا أنّ هذه التغييرات سيتمّ حفظها في قاعدة البيانات، ولكن سيتمّ النظر إليها على أنها البيانات الأصليّة للسجل، ولن يمكنك التراجع عنها.. لاحظ أن هذه الوسيلة تفعل ما يلي:


صفحة الشاعر