VB
Dim var As Boolean
var = True
var = False
C#
bool var;
var = true;
var = false;
ومن منظور قاعدة البيانات فهو يماثل النوع bit في SQL Server او True/False في Access.
من ناحية اخرى، عندما نتوقع مجموعة محددة ومعروفة لمجال القيم، فان المبرمجين يفضلون تعريف تركيبات من نوع enum (تسمى في عالم البرمجة Enumerator Types)، فمثلا هذا تركيب يمثل مستوى الطالب الجامعي:
VB
Public Enum StudentLevelENM
Orientation
Freshman
Sophomore
Junior
Senior
End Enum
...
...
Dim Heema, Ali, Mohd As StudentLevelENM
Heema = StudentLevelENM.Freshman
Ali = StudentLevelENM.Sophomore
Mohd = StudentLevelENM.Senior
C#:
public enum StudentLevelENM
{
Orientation,
Freshman,
Sophomore,
Junior,
Senior
}
...
...
StudentLevelENM Heema, Ali, Mohd;
Heema = StudentLevelENM.Freshman;
Ali = StudentLevelENM.Sophomore;
Mohd = StudentLevelENM.Senior;
تقنيا، التركيبات من نوع enum هي عددية من النوع Integer (يمكنك تغيير النوع رغم انك غير محتاج لتغييره غالبا)، عناصر التركيب تبدأ بالرقم 0 وتزيد بواحد، مع ذلك يمكنك تخصيص القيم وفقا لاحتياجاتك، فيمكننا مثلا تعريف تركيب يمثل الجنس Gender
(والذي –كما هو معلوم- يكون اما ذكر، أنثى، او أنثى جميلة):
Basic:
Public Enum GenderENM
Male = 10
Female = 20
BeautifulFemale = 30
End Enum
...
...
Dim Turki, NaDa As GenderENM
Turki = GenderENM.Male
NaDa = GenderENM.BeautifulFemale
C#:
public enum GenderENM
{
Male = 10,
Female = 20,
BeautifulFemale = 30
}
...
...
GenderENM Turki, NaDa;
Turki = GenderENM.Male ;
NaDa = GenderENM.BeautifulFemale;
التركيبات البتية Bit-Coded Enums:
من الامثلة السابقة رأينا ان كل متغير تعرفه من التركيب يمكن ان تسند اليه قيمة ((واحدة فقط)) من احد عناصر التركيب، ولكنك في حالات كثيرة تود ان تدمج اكثر من قيمة، فمثلا لو اردنا معرفة لغات البرمجة التي يتقنها شخص:
Basic:
Public Enum LanguagesENM
NoLanguage
VisualBasic
CSharp
CPlusPlus
Java
Delphi
End Enum
...
...
Dim Maram, NooRa, Loly As LanguagesENM
Maram = LanguagesENM.VisualBasic
NooRa = LanguagesENM.Delphi
Loly = LanguagesENM.CSharp
C#:
public enum LanguagesENM
{
NoLanguage,
VisualBasic,
CSharp,
CPlusPlus,
Java,
Delphi
}
...
...
LanguagesENM Maram, NooRa, Loly;
Maram = LanguagesENM.VisualBasic;
NooRa = LanguagesENM.Delphi;
Loly = LanguagesENM.CSharp;
فالمشكلة اننا لا نستطيع ان نسند اكثر من لغة لكل شخص، والحل (الغير احترافي) يقتضي بالغاء فكرة التركيبات وتحويلها الى متغيرات من النوع Boolean (ونفس الشيء في قاعدة البيانات) لكل لغة:
Basic:
Dim IsVisualBasic As Boolean
Dim IsCSharp As Boolean
Dim IsCPlusPlus As Boolean
Dim IsJava As Boolean
Dim IsDelphi As Boolean
C#:
bool IsVisualBasic;
bool IsCSharp;
bool IsCPlusPlus;
bool IsJava;
bool IsDelphi;
مع ذلك لا ينصح ابدا باتباع هذا الاسلوب الممل حيث سيستهلك الكثير من الوقت واستنزاف اكثر للموارد، اما الحل الاحترافي فهو بالاعتماد على التركيبات البتية Bit-Coded Enumerators (تسمى ايضا Bitwise Enumerators) والتي تمكنك من محاكاة عملية اسناد اكثر من قيمة الى متغير من نوع التركيب.
في الحقيقة، التركيبات البتية ليست اختراعا جديدا ولا ميزة اضافية في لغة البرمجة، فكل التركيبات يمكن ان تكون تركيبات بتية شريطة ان يتم ترقيم عناصرها بطريقة خاصة، والطريقة الخاصة تقول قيمة العنصر = 2^(ترتيب العنصر-1) بإستثناء العنصر الاول
فلابد ان يكون دائما صفر، فتركيب لغات البرمجة LanguagesENM السابق سيكون:
Basic:
Public Enum LanguagesENM
NoLanguage = 0 ' 0
VisualBasic = 1 ' 2^0
CSharp = 2 ' 2^1
CPlusPlus = 4 ' 2^2
Java = 8 ' 2^3
Delphi = 16 ' 2^4
End Enum
C#:
public enum LanguagesENM
{
NoLanguage = 0, // 0
VisualBasic = 1, // 2^0
CSharp = 2, // 2^1
CPlusPlus = 4, // 2^2
Java = 8, // 2^3
Delphi = 16 // 2^4
}
يمكننا بكل ثقة ان نقول الآن بأن التركيب LanguagesENM هو تركيب بتي Bit-Coded، ولكن من المفيد تكحيله بالمواصفة Flags Attribute (حتى تفهم باقي فئات اطار عمل .NET Framework بأنه تركيب بتي خاصة مع الطريقة ToString() ):
Basic:
<Flags()> _
Public Enum LanguagesENM
NoLanguage = 0 ' 0
VisualBasic = 1 ' 2^0
...
...
C#:
[Flags()]
public enum LanguagesENM
{
NoLanguage = 0, // 0
VisualBasic = 1, // 2^0
...
...
التعامل مع التركيبات البتية:
التركيبات البتية تعتمد على المنطق بشكل جنوني، واي خطأ او عدم فهم لفكرتها ستؤدي بك الى كوارث برمجية لا يعلم عقباها احد، تبدأ عملية التعامل باسناد القيم والذي يمكنك استخدام المعامل البتي Or (او | في C#):
Basic:
Dim Ibrahim As LanguagesENM
Ibrahim = LanguagesENM.VisualBasic Or LanguagesENM.CSharp
Console.WriteLine(Ibrahim.ToString()) ' Visual Basic, CSharp
C#:
LanguagesENM Ibrahim;
Ibrahim = LanguagesENM.VisualBasic | LanguagesENM.CSharp;
Console.WriteLine(Ibrahim); // Visual Basic, CSharp
تحذير: إياك ثم إياك ان تستخدم المعاملات المنطقية AndAlso او OrElse، فهي تقوم بتحويل القيم الى Boolean دون اختبار بتات القيم Value Bits.
منطقيا، تكرار اسناد نفس القيم لا يقدم ولا يؤخر وكأن شيئا لم يحدث:
Basic:
Ibrahim = LanguagesENM.VisualBasic Or LanguagesENM.CSharp Or LanguagesENM.VisualBasic
Console.WriteLine(Ibrahim.ToString()) ' Visual Basic, CSharp
C#:
Ibrahim = LanguagesENM.VisualBasic | LanguagesENM.CSharp | LanguagesENM.VisualBasic;
Console.WriteLine(Ibrahim); // Visual Basic, CSharp
عندما تنوي ((اضافة)) قيمة جديدة الى متغير من نوع التركيب، لا تنسى استخدام القيمة الاصلية:
Basic:
Ibrahim = Ibrahim Or LanguagesENM.Delphi
Console.WriteLine(Ibrahim); // Visual Basic, CSharp, Delphi
C#:
Ibrahim = Ibrahim | LanguagesENM.Delphi;
Console.WriteLine(Ibrahim); // Visual Basic, CSharp, Delphi
كان هذا حول اسناد القيم، اما عملية قراءة القيم فتستخدم المعامل And (المعامل & في C#)عند عملية التحقق مع ((القيمة الاصلية)) للمتغير، فلو أردنا معرفة هل Ibrahim مبرمج بلغة Visual Basic قد نكتب شرطا شبيها بـ:
Basic:
If CBool(Ibrahim And LanguagesENM.VisualBasic) Then
Console.WriteLine("True")
End If
C#:
if (Convert.ToBoolean(Ibrahim & LanguagesENM.VisualBasic))
Console.WriteLine("True");
بنفس الاسلوب السابق يمكننا معرفة كافة القيم الاخرى:
Basic:
Console.WriteLine("Visual Basic: " & CBool(Ibrahim And LanguagesENM.VisualBasic))
Console.WriteLine("CSharp: " & CBool(Ibrahim And LanguagesENM.CSharp))
Console.WriteLine("CPlusPlus: " & CBool(Ibrahim And LanguagesENM.CPlusPlus))
Console.WriteLine("Java: " & CBool(Ibrahim And LanguagesENM.Java))
Console.WriteLine("Delphi: " & CBool(Ibrahim And LanguagesENM.Delphi))
C#:
Console.WriteLine("Visual Basic: " + Convert.ToBoolean(Ibrahim & LanguagesENM.VisualBasic));
Console.WriteLine("CSharp: " + Convert.ToBoolean(Ibrahim & LanguagesENM.CSharp));
Console.WriteLine("CPlusPlus: " + Convert.ToBoolean(Ibrahim & LanguagesENM.CPlusPlus));
Console.WriteLine("Java: " + Convert.ToBoolean(Ibrahim & LanguagesENM.Java));
Console.WriteLine("Delphi: " + Convert.ToBoolean(Ibrahim & LanguagesENM.Delphi));
مخرجات الكود السابق يتوقع ان تكون:
Visual Basic: True
CSharp: True
CPlusPlus: False
Java: False
Delphi: True
من ناحية اخرى، يمكنك الغاء قيمة من قيم التركيب باستخدام الرابط And مع معامل النفي Not (الرابط & والنفي ~ في لغة C#):
Basic:
Ibrahim = Ibrahim And Not LanguagesENM.CSharp
Console.WriteLine(Ibrahim.ToString()) ' Visual Basic, Delphi
C#:
Ibrahim = Ibrahim & ~LanguagesENM.CSharp;
Console.WriteLine(Ibrahim); // Visual Basic, Delphi
اخيرا، يمكنك استخدام الرابط XOR ((لعكس)) وجود القيمة، بمعنى ان كانت القيمة موجودة سيتم الغائها وان لم تكن سيتم اضافتها:
Basic:
Ibrahim = Ibrahim Xor LanguagesENM.Delphi
Ibrahim = Ibrahim Xor LanguagesENM.CPlusPlus
Console.WriteLine(Ibrahim.ToString()) ' Visual Basic, CPlusPlus
C#:
Ibrahim = Ibrahim ^ LanguagesENM.Delphi;
Ibrahim = Ibrahim ^ LanguagesENM.CPlusPlus;
Console.WriteLine(Ibrahim); // Visual Basic, CPlusPlus
خاتمة
كما رأيت، فان التركيبات البتية Bit-Coded Enums ليست سوى تركيبات تحمل قيم مرتبة بطريقة خاصة تمكننا من تمثيل مجموعة قيم في مكان وواحد (سواء متغير او حقل جدولي بقاعدة بيانات)، نعتمد على المنطق بشكل حذر لتعديل وقراءة القيم، فأي خطأ قد يؤدي الى شوائب واخطاء خطيرة في البرنامج.
اجراءات Windows API وفئات اطار عمل .NET Framework والكثير من مكتبات الفئات Class Libraries تعتمد على التركيبات البتية بشكل كبير جدا، وهناك آلاف الأمثلة التطبيقية لها (لعل ابرزها دالة MessageBox).
-- تركي