الدليل السريع للغة البرمجة #C
لغة C# هي لغة برمجة أنيقة، كائنيّة التوجه Object-oriented بأنواع بيانات سليمة Type-safe تمكّن المطورين من بناء تطبيقات آمنة ومتينة تعمل على إطار العمل NET.
// تبدأ التعليقات وحيدة السطر بشريطين مائلين هكذا //
/* توضع التعليقات متعدّدة الأسطر بين العلامة أعلاه والعلامة أسفله */
هذا تعليق xml يُستخدَم لتوليد توثيق خارجي
أو لتقديم مساعدة حسب السياق في بيئة تطوير مندمجة IDE.
/// <param name="firstParam"> لتوثيق الدالة Parameter الذي هو معامل firstParam هذا تعليق</param>
/// <returns>معلومات عن القيمة المُرجَعة للدالة/returns>
//public void MethodOrClassOrOtherWithParsableHelp(string firstParam) {}
يحدّد فضاءات اﻷسماء Namespaces
التي ستستخدمها هذه الشفرة
فضاءات الأسماء أدناه هي كلّها جزء من مكتبة الأصناف Classes المعيارية في إطار العمل NET.
Framework Class Library using System; using System.Collections.Generic; using System.Dynamic; using System.Linq; using System.Net; using System.Threading.Tasks; using System.IO;
فضاء الأسماء هذا ليس مُتضمّنا في المكتبة المعيارية:
using System.Data.Entity;
لكي تتمكّن من استخدام المكتبة أعلاه فستحتاج لإضافة مرجع إلى ملف dll
وهو ما يمكن لمدير الحزم NuGet فعلُه: Install-Package EntityFramework
تعرّف فضاءات الأسماء مجالات لتنظيم الشفرات ضمن حزم Packages أو وِحْدات Modules لاستخدام فضاء الأسماء المُعرّف أدناه في شفرة أخرى نضيف العبارة Learning.CSharp
إلى فضاءات الأسماء المستخدمة
namespace Learning.CSharp {
يجب أن يحوي كل ملف cs.
على الأقل على صنف Class له نفس اسم الملف. يمكنك لك عدم التقيّد بهذا الشرط، إلا أنه أفضل لصحة الشفرة المصدرية
public class LearnCSharp {
صياغة أساسية: يمكنك التجاوز إلى “ميزات مثيرة للاهتمام” إن سبق لك كتابة شفرات بجافا أو سي++
public static void Syntax() { // للكتابة في سطر جديد Console.WriteLine استخدم Console.WriteLine("Hello World"); Console.WriteLine( "Integer: " + 10 + " Double: " + 3.14 + " Boolean: " + true); // لكتابة عبارات على نفس السطر Console.Write استخدم Console.Write("Hello "); Console.Write("World");
أنواع البيانات Types والمتغيّرات Variables
عرّف المتغيّرات على النحو التالي <type> <name>
Sbyte
– عدد صحيح (سالب أو موجب) على 8 بتات (محصور بين 128- و127)
sbyte fooSbyte = 100;
Byte
– عدد طبيعي (موجب فقط) على 8 بتات (محصور بين 0 و255)
byte fooByte = 100;
Short
– عدد صحيح أو طبيعي طوله 16 بتات
صحيح short
محصور بين -32,768 و32,767
طبيعي ushort
محصور بين 0 و65,535
short fooShort = 10000; ushort fooUshort = 10000;
عدد صحيح fooInt
أو طبيعي fooUint
طوله 32 بت
int fooInt = 1; // (-2,147,483,648 <= int <= 2,147,483,647) uint fooUint = 1; // (0 <= uint <= 4,294,967,295)
Long
عدد صحيح fooLong
أو طبيعي fooUlong
طوله 64 بت
long fooLong = 100000L; // (-9,223,372,036,854,775,808 <= long <= 9,223,372,036,854,775,807) ulong fooUlong = 100000L; // (0 <= ulong <= 18,446,744,073,709,551,615)
النوع المبدئي default
للأعداد هو int
أو uint
حسب طول العدد. والحرف L
وراء العدد يشير إلى أن نوع العدد هو long
أو ulong
Double
– فاصلة عائمة مزدوجة الدقة حسب المعيار 64-bit IEEE 754
double fooDouble = 123.4; // الدقة: 15-16 رقما
Float
– فاصلة عائمة وحيدة الدقة 32-bit IEEE 754 Floating Point
float fooFloat = 234.5f; // الدقة: 7 أرقام
يشير الحرف f
وراء العدد إلى أن نوع العدد هو Float
Decimal
– نوع بيانات بطول 128 بت، ودقّة أعلى من بقية أنواع البيانات ذات الفاصلة العائمة مناسب للحسابات المالية والنقدية
decimal fooDecimal = 150.3m; // Boolean - true & false bool fooBoolean = true; // or false
Char
– نوع بيانات بطول 16 بت يرمز لمحرف يونيكود
`char fooChar = 'A';
Strings
– على النقيض من جميع أنواع البيانات السابقة التي هي أنواع لقيم البيانات
فإن النوع String
– سلسلة محارف – هو نوع لمرجع Reference
بمعنى أنه يمكنه أخذ القيمة null
string fooString = ""escape" quotes and add n (new lines) and t (tabs)"; Console.WriteLine(fooString);
يمكن الوصول إلى كل محرف من سلسلة المحارف عن طريق ترتيبه في السلسلة
char charFromString = fooString[1]; // => 'e'
لا يمكن التعديل على سلاسل المحارف؛ التعليمة fooString[1] = X
خاطئة مقارنة سلاسل محارف مع قيمة الخاصيّة CurrentCulture
المعرّفة في المكتبة المعيارية لتمثيل اللغة المستخدمة في النظام، مع تجاهل حالة الأحرف IgnoreCase
string.Compare(fooString, "x", StringComparison.CurrentCultureIgnoreCase);
تهيئة سلسلة المحارف اعتمادا على sprintf
string fooFs = string.Format("Check Check, {0} {1}, {0} {1:0.0}", 1, 2);
التاريخ والتهيئة
DateTime fooDate = DateTime.Now; Console.WriteLine(fooDate.ToString("hh:mm, dd MMM yyyy"));
سلاسل المحارف الأصلية Verbatim String
يمكنك استخدام العلامة @
أمام سلسلة محارف لتخليص جميع المحارف الموجودة في السلسلة
string path = "C:\Users\User\Desktop"; string verbatimPath = @"C:UsersUserDesktop"; Console.WriteLine(path == verbatimPath); // => true
يمكنك توزيع سلسلة محارف على أكثر من سطر بالرمز @
لتخليص العلامة "
ضع مكانها ""
("
مرتين)
string bazString = @"Here's some stuff on a new line! ""Wow!"", the masses cried";
استخدم الكلمة المفتاحية const
لجعل المتغيّر ثابتًا غير قابل للتعديل وتُحسب القيم الثابتة أثناء تصريف البرنامج compile time
const int HoursWorkPerWeek = 9001;
بنى البيانات
المصفوفات – يبدأ العنصر الأول عند الترتيب 0
ويجب تحديد قياس المصفوفة عند تعريفها صيغة تعريف المصفوفة هي كالتالي:
;<datatype>[] <var name> = new <datatype>[<array size>]
المصفوفة intArray
في المثال التالي تحوي 10 أعداد
int[] intArray = new int[10];
طريقة أخرى لتعريف مصفوفة وتهيئتها
int[] y = { 9000, 1000, 1337 };
ترتيب عناصر المصفوفة – الوصول إلى عنصر
Console.WriteLine("intArray @ 0: " + intArray[0]);
المصفوفات قابلة للتعديل.
intArray[1] = 1;
القوائم
تُستخدَم القوائم أكثر من المصفوفات لما توفّره من مرونة وصيغة تعريف قائمة هي على النحو التالي:
;List<datatype> <var name> = new List<datatype>()
List<int> intList = new List<int>(); List<string> stringList = new List<string>(); List<int> z = new List<int> { 9000, 1000, 1337 }; // تحديد القيم الابتدائية لعناصر القائمة
تُستخدَم الإشارتان <>
للأنواع العميمة Generics – راجع فقرة ميزات رائعة
ليست للقوائم قيم مبدئية ويجب أولا إضافة قيمة قبل إمكانية الوصول إلى العنصر
intList.Add(1); Console.WriteLine("intList @ 0: " + intList[0]);
بنى تحتية أخرى يجدر بك مراجعتها:
قوائم الانتظار Queues/ الرصوص Stacks
القواميس DictionariesHashSet
تجميعاـ القراءة فقط Read-only collections
الأزواج المُرتّبة Tuples (الإصدار 4 من .NET. فما فوق)
العوامل
Console.WriteLine("n→Operators"); int i1 = 1, i2 = 2; // اختصار لتعريف متغيّرات عدة في آن واحد
العمليات الحسابية واضحة
Console.WriteLine(i1 + i2 - i1 * 3 / 7); // => 3
المقياس Modulo
Console.WriteLine("11%3 = " + (11 % 3)); // => 2
عوامل المقارنة
Console.WriteLine("3 == 2? " + (3 == 2)); // => false Console.WriteLine("3 != 2? " + (3 != 2)); // => true Console.WriteLine("3 > 2? " + (3 > 2)); // => true Console.WriteLine("3 < 2? " + (3 < 2)); // => false Console.WriteLine("2 <= 2? " + (2 <= 2)); // => true Console.WriteLine("2 >= 2? " + (2 >= 2)); // => true
عوامل المقارنة البتّية Bitwise
~
عامل التكملة الأحادي (إن كان البت يحوي 0 يحوله إلى 1، وإن كان يحوي واحد يحوّله إلى صفر)>>
إزاحة البتات إلى اليسار<<
إزاحة البتات إلى اليمين&
عامل “و” المنطقي^
عامل “أو” المنطقي غير الشامل exclusive OR|
عامل “أو” المنطقي الشامل inclusive OR
التزايد Incrementation
int i = 0; Console.WriteLine("n->Inc/Dec-rementation"); Console.WriteLine(i++); //Prints "0", i = 1. تزاد بعدي Console.WriteLine(++i); //Prints "2", i = 2. تزايد قبلي Console.WriteLine(i--); //Prints "2", i = 1. تناقص بعدي Console.WriteLine(--i); //Prints "0", i = 0. تناقص قبلي
بنى التحكّم
Console.WriteLine("n->Control Structures");
تتبع بنية التحكم if else
طريقة كتابة بنى التحكم في C
int j = 10; if (j == 10) { Console.WriteLine("I get printed"); } else if (j > 10) { Console.WriteLine("I don't"); } else { Console.WriteLine("I also don't"); }
العوامل الثلاثية
بنية تحكّم if else
بسيطة تمكن كتابتها على النحو التالي:
<condition> ? <true> : <false>
int toCompare = 17; string isTrue = toCompare == 17 ? "True" : "False";
حلقة While التكرارية
int fooWhile = 0; while (fooWhile < 100) { //تتكرّر الحلقة مئة مرة، من القيمة 0 إلى القيمة 99 fooWhile++; }
حلقة Do.. While
التكرارية
int fooDoWhile = 0; do {
الحلقة معدّة للتكرار مئة مرة، من القيمة 0 إلى القيمة 99
Start iteration 100 times, fooDoWhile 0->99 if (false) continue; // تجاوز التكريرة الحالية fooDoWhile++; if (fooDoWhile == 50) break; // توقيف الحلقة تماما، والخروج منها } while (fooDoWhile < 100);
حلقة for
التكرارية ذات الصيغة:
(<for(<start_statement>; <conditional>; <step
for (int fooFor = 0; fooFor < 10; fooFor++) { // تتكرّر الحلقة عشر مرات، من القيمة 0 إلى القيمة 9 }
حلقة For Each
يمكن استخدام حلقة التكرار foreach
للمرور عبر أي كائن Object
يُنفّذ الصنف IEnumerable
أو <IEnumerable<T
تنفّذ جميع الأنواع التجميعية (المصفوفات، القوائم، القواميس…) في إطار العمل .Net واجهة أو أكثر من الأصناف المذكورة
(يمكن حذف ()ToCharArray
من التعليمة أدناه، لأن String
تنفّذ الواجهة IEnumerable
)
foreach (char character in "Hello World".ToCharArray()) { // تمرّ على جميع المحارف في السلسلة }
تعليمة Switch
تعمل Switch
مع أنواع البيانات byte
, short
, char
, وint
تعمل كذلك مع أنواع البيانات Enum
(نتعرّض لها أدناه)، الصنف String
وبضعة أصناف خاصّة تغلّف أنواع بيانات أساسية: Character
,Byte
,Short
, و Integer
.
int month = 3; string monthString; switch (month) { case 1: monthString = "January"; break; case 2: monthString = "February"; break; case 3: monthString = "March"; break;
يمكن تنفيذ أكثر من إجراء في كل حالة case
، إلا أنه لا يمكن إضافة إجراء ضمن حالة دون إضافة تعليمة توقيف break; قبل الحالة الموالية (إن أردت فعل هذا الأمر، فستحتاج لإضافة تعليمة goto case x
بعد الإجراء)
case 6: case 7: case 8: monthString = "Summer time!!"; break; default: monthString = "Some other month"; break; }
التحويل بين أنواع البيانات وجبْر الأنواع Typecasting
تحويل البيانات
تحويل سلسلة محارف String إلى عدد Integer
سيظهر استثناء Exception في حالة إخفاق عملية التحويل
int.Parse("123");// نحصُل على النسخة العددية من سلسلة المحارف "123"
عند استخدام الدالة TryParse
لتحويل نوع البيانات فإن قيمة التحويل ستكون القيمة المبدئية لنوع البيانات وفي حالة الأعداد فإن القيمة المبدئية هي 0
int tryInt; if (int.TryParse("123", out tryInt)) // ترجع الدالة قيمة منطقية Console.WriteLine(tryInt); // 123
تحويل الأعداد إلى سلاسل محارف String
يتضمّن الصنف Convert عددا من التوابع Methods لتسهيل التحويل
Convert.ToString(123);
أو
tryInt.ToString();
جبر أنواع البيانات
جبر العدد العشري 15 للحصول على قيمة من النوع int
ثم جبر القيمة المُتحصَّل عليها ضمنيا لنحصُل على النوع long
long x = (int) 15M; }
الأصناف
راجع التعريفات في آخر الملف
public static void Classes() {
انظر تعريف الكائنات في آخر الملف
استخدم الكلمة المفتاحية new لاستهلال صنف
Bicycle trek = new Bicycle();
استدعاء توابع الكائن
trek.SpeedUp(3); // يجب دائما المرور عبر المعدّلات والمسترجعات Setter and getter methods trek.Cadence = 100;
يُستخدم التابع ToString
لعرض قيمة الكائن
Console.WriteLine("trek info: " + trek.ToString());
استهلال كائن جديد من الصنف PennyFarthing
PennyFarthing funbike = new PennyFarthing(1, 10); Console.WriteLine("funbike info: " + funbike.ToString()); Console.Read(); } // نهاية التابع الرئيس Main method
مَدخل الكونسول Console entry
. التطبيقات التي تعمل عبر الطرفية يجب أن يكون لديها مدخل عبارة عن تابع رئيس
public static void Main(string[] args) { OtherInterestingFeatures(); }
ميزات مثيرة للاهتمام
التوقيعات المبدئية للتوابع
public // مجال الرؤية static // يسمح بالاستدعاء المباشر من الصنف دون المرور بكائنات int // نوع البيانات المُرجَعة, MethodSignatures( int maxCount, // المتغيّر الأول عددي int count = 0, // القيمة المبدئية هي 0، تُستخدَم إن لم يُمرَّر متغير إلى التابع int another = 3, params string[] otherParams // يستقبل بقية المتغيّرات المُمررة إلى التابع جميعا ) { return -1; }
يمكن أن تكون أسماء التوابع متطابقة، ما دامت التوقيعات مختلفة وكل تابع لا يختلف عن آخر سوى في نوع البيانات المُرجَع ليس وحيدا
public static void MethodSignatures( ref int maxCount, // تمرير المعاملات حسب المرجع، وليس القيمة out int count) {
المعامل المُمرر في المتغيّر count
سيحوي القيمة 15 خارج هذه الدالة
count = 15; // معامل الخروج out يجب أن يُسنَد قبل الانتهاء من التابع }
أنواع البيانات العميمة Generics
الأصناف TKey
وTValue
يحدّدها المستخدم الذي يستدعي هذه الدالة ويحاكي هذا التابع عمل SetDefault
في بايثون
public static TValue SetDefault<TKey, TValue>( IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultItem) { TValue result; if (!dictionary.TryGetValue(key, out result)) return dictionary[key] = defaultItem; return result; }
يمكنك تقييد الكائنات التي يمكن تمريرها إلى الدالة
public static void IterateAndPrint<T>(T toPrint) where T: IEnumerable<int> {
بما أن الصنف T
ينفّذ IEnumerable
فإنه يمكننا المرور على عناصره باستخدام foreach
foreach (var item in toPrint) // العنصر هو من النوع int Console.WriteLine(item.ToString()); }
الكلمة المفتاحية “yield”
يدلّ استخدام yield
أن التابع الذي تظهر فيه هذه الكلمة المفتاحية لديه خاصيّة التكرار (أي أنه يمكن استخدام التابع مع الحلقة foreach
)
public static IEnumerable<int> YieldCounter(int limit = 10) { for (var i = 0; i < limit; i++) yield return i; }
نستطيع استدعاء التابع أعلاه على النحو التالي
public static void PrintYieldCounterToConsole() { foreach (var counter in YieldCounter()) Console.WriteLine(counter); }
يمكن استخدام yield return
أكثر من مرّة في نفس التابع
public static IEnumerable<int> ManyYieldCounter() { yield return 0; yield return 1; yield return 2; yield return 3; }
كما يمكنك استخدام “yield break” لتوقيف التكرار
التابع التالي يُرجِع نصف القيم الموجودة بين 0 وlimit
public static IEnumerable<int> YieldCounterWithBreak(int limit = 10) { for (var i = 0; i < limit; i++) { if (i > limit/2) yield break; yield return i; } } public static void OtherInterestingFeatures() {
المعاملات الاختيارية
MethodSignatures(3, 1, 3, "Some", "Extra", "Strings"); MethodSignatures(3, another: 3); // تعيين قيمة المعامل مباشرة، مع تجاوز المعاملات الاختيارية
تمرير المعاملات بالمرجع By reference
، والقيمة المُرجعة Out parameter
BY REF AND OUT PARAMETERS int maxCount = 0, count; // المعاملات المُمررة بالمرجع يجب أن تحوي قيمة MethodSignatures(ref maxCount, out count);
توابع التمديد Extension methods
int i = 3; i.Print(); // مُعرَّفة أدناه
الأنواع التي تقبل قيمة فارغة Nullable types، مناسبة للتخاطب مع قواعد البيانات والقيم المُرجَعة وأي نوع بيانات قيمي (أي ليس صنفا) يمكن جعله يقبل قيما فارغة بكتابة ?
بعده
<type>? <var name> = <value>
int? nullable = null; // اختصار لـ Nullable<int> Console.WriteLine("Nullable variable: " + nullable); bool hasValue = nullable.HasValue; // قيمة منطقية صحيحة true إن لم يكن يساوي null
علامتا الاستفهام المتلاصقتان ??
هما اختصار لتحدد قيمة مبدئية في حال كان المتغيّر فارغا نعطيه 0 قيمة مبدئية
int notNullable = nullable ?? 0; // 0
?.
هذه العلامة عي عامل للتحقّق من القيمة الفارغة null
nullable?.Print(); // استخدم تابع التمديد Print() إذا كان المتغيّر nullable مختلفا عن null
المتغيّرات ضمنية النوع – يمكنك ترك المُصرّف Compiler يحدّد نوع المتغيّر:
var magic = "magic is a string, at compile time, so you still get type safety";
;magic = 9
لن تُسنَد القيمة 9 إلى المتغيّر magic
لأنه يحوي سلسلة محارف
الأنواع العميمة
var phonebook = new Dictionary<string, string>() { {"Sarah", "212 555 5555"} // إضافة عنوان إلى دفتر العناوين };
استدعاء الدالة SetDefault المُعرَّفة أعلاه
Console.WriteLine(SetDefault<string,string>(phonebook, "Shaun", "No Phone")); // No Phone
يمكنك عدم تحديد TKey
و TValue
بما أنه يمكن استنتاجهما تلقائيا
Console.WriteLine(SetDefault(phonebook, "Sarah", "No Phone")); // 212 555 5555
الدوال مجهولة الاسم Lambda expressions
– تتيح كتابة شفرات على نفس السطر
Func<int, int> square = (x) => x * x; // آخر عنصر من T هو القيمة المُرجعة Console.WriteLine(square(3)); // 9
التعامل مع الأخطاء
try { var funBike = PennyFarthing.CreateWithGears(6);
لن تُنفَّذ لأن CreateWithGears
تتسبّب في استثناء Exception
string some = ""; if (true) some = null; some.ToLower(); // تتسبّب في الاستثناء NullReferenceException } catch (NotSupportedException) { Console.WriteLine("Not so much fun now!"); } catch (Exception ex) // التقاط جميع الاستثناءات الأخرى { throw new ApplicationException("It hit the fan", ex); // throw; // التقاط آخر يحافظ على ركام النداء callstack } // catch { } // التقاط كل شيء دون التعامل مع الاستثناءات finally { // try أو catch تُنفّذ بعد }
إدارة الموارد
يمكنك إدارة الموارد المتوفّرة بسهولة حيث تنفّذ أغلب الكائنات التي تستعمل الموارد غير المستغلة (الملفات، سياق الأجهزة الطرفية، …إلخ) تُنفّذ الواجهة IDisposable
تتولّى التعليمة using
التخلّص من كائنات IDisposable
using (StreamWriter writer = new StreamWriter("log.txt")) { writer.WriteLine("Nothing suspicious here"); }
يُتخلَّص من جميع الموارد بعد الانتهاء من تنفيذ هذه الشفرة حتى ولو تسبّبت في استثناء
البرمجة المتوازية
var words = new List<string> {"dog", "cat", "horse", "pony"}; Parallel.ForEach(words, new ParallelOptions() { MaxDegreeOfParallelism = 4 }, word => { Console.WriteLine(word); } );
تشغيل هذه الشفرة سينتُج عنه مُخرجات متعدّدة لأن كلّ تشعّب thread يُكمل في وقت مختلف عن الآخر. أدناه أمثلة على المُخرجات
cat dog horse pony
dog horse pony cat
الكائنات الديناميكية (رائعة للعمل مع لغات برمجة أخرى)
dynamic student = new ExpandoObject(); student.FirstName = "First Name";
لا تحتاج لتعريف صنف أولا بل إنه يمكنك إضافة توابع (يُرجع التابع أدناه سلسلة محارف ويتلقّى سلسلة محارف)
student.Introduce = new Func<string, string>( (introduceTo) => string.Format("Hey {0}, this is {1}", student.FirstName, introduceTo)); Console.WriteLine(student.Introduce("Beth"));
تنفّذ أغلب التجميعات Collections
الواجهة <IQUERYABLE<T
التي توفّر الكثير من التوابع المفيدة
var bikes = new List<Bicycle>(); // دراجات هوائية bikes.Sort(); // يرتّب القائمة bikes.Sort((b1, b2) => b1.Wheels.CompareTo(b2.Wheels)); // يرتّب القائمة بناءً على عدد العجلات var result = bikes .Where(b => b.Wheels > 3) // الترشيج والفلترة .Where(b => b.IsBroken && b.HasTassles) .Select(b => b.ToString()); // var sum = bikes.Sum(b => b.Wheels);
يجمع عدد العجلات في كامل القائمة وينشئ قائمة من الكائنات الضمنية Implicit objects بالاعتماد على بعض خواص الدراجة الهوائية
var bikeSummaries = bikes.Select(b=>new { Name = b.Name, IsAwesome = !b.IsBroken && b.HasTassles });
من الصعب توضيح الأمر هنا، إلا أنك تحصُل على نوع البيانات قبل الانتهاء من التعليمات، إذ أن المصرّف يمكنه العمل ضمنا على الأنواع أعلاه
foreach (var bikeSummary in bikeSummaries.Where(b => b.IsAwesome)) Console.WriteLine(bikeSummary.Name);
التوازي مع ASPARALLEL
نخلط عمليّات LINQ
والعمليّات المتوازية
var threeWheelers = bikes.AsParallel().Where(b => b.Wheels == 3).Select(b => b.Name);
سيحدث الأمر بالتوازي. تُنشَأ التشعبات تلقائيا وستُقسَّم النتائج بينها
طريقة رائعة للعمل مع مجموعة بيانات ضخمة إن كانت لديك الكثير من الأنوية Cores
LINQ
تربط بين مخزن بيانات وكائنات من الصنف <IQueryable<T
مثلا: LinqToSql
تربط الكائنات مع قاعدة بيانات، LinqToXml
تربط الكائنات مع مستند XML
var db = new BikeRepository();
يؤجَّل التنفيذ، وهو أمر جيّد عند التعامل مع قواعد البيانات
var filter = db.Bikes.Where(b => b.HasTassles); // no query run if (42 > 6) // يمكنك الاستمرار في إضافة المرشحات، حتى تلك المشروطة؛ مناسبة لميزة "البحث المتقدّم" filter = filter.Where(b => b.IsBroken); // no query run var query = filter .OrderBy(b => b.Wheels) .ThenBy(b => b.Name) .Select(b => b.Name); // still no query run
يعمل الاستعلام الآن، إلا أنك لا تحصُل على نتائج الاستعلام إلا عند المرور عليها
foreach (string bike in query) Console.WriteLine(result); } } // نهاية الصنف LearnCSharp
يمكنك إضافة أصناف أخرى في ملف cs.
public static class Extensions {
توابع الصنف Extensions
public static void Print(this object obj) { Console.WriteLine(obj.ToString()); } }
التفويض والأحداث
public class DelegateTest { public static int count = 0; public static int Increment() { // زيادة العدّاد ثم إرجاع النتيجة return ++count; }
التفويض delegate
هو مرجع لتابع
لجعل مرجع على التابع Increment
نبدأ بتعريف تفويض بنفس التوقيع أي أنه لا يأخذ أية معطيات ويُرجع عددا من النوع int
public delegate int IncrementDelegate();
يمكن أيضا استخدام حدث Event
لتحريك التفويض
أنشئ حدثا بنوع التفويض
public static event IncrementDelegate MyEvent; static void Main(string[] args) {
نحيل إلى التابع Increment
باستهلال التفويض وتمرير معطى هو التابع نفسه
IncrementDelegate inc = new IncrementDelegate(Increment); Console.WriteLine(inc()); // => 1
يمكن تركيب التفويضات بالعامل +
IncrementDelegate composedInc = inc; composedInc += inc; composedInc += inc;
سينفّذ التفويض composedInc
التابع Increment
ثلاث مرات
Console.WriteLine(composedInc()); // => 4
الاشتراك في الحدث باستخدام التفويض
MyEvent += new IncrementDelegate(Increment); MyEvent += new IncrementDelegate(Increment);
تحريك الحدث، أي تنفيذ كل التفويضات المشترِكة في هذا الحدث
Console.WriteLine(MyEvent()); // => 6 } }
صيغة تعريف صنف:
<public/private/protected/internal> class <class name>{` // حقول البيانات، المشيّدات، الدوالّ.. كلّها في الداخل //تُستدعى الدوال بنفس طريقة استدعاء التوابع في جافا }
public class Bicycle { // حقول/متغيّرات صنف الدراجات الهوائية Bicycle public int Cadence // عمومي public : يمكن استدعاء من أي مكان { get // مسترجع - نعرّف تابعا للوصول إلى قيمة خاصيّة من الكائن { return _cadence; } set // معدّل - نعرّف تابعا لتعيين قيمة خاصيّة { _cadence = value; // القيمة value هي المعطى المُمرّر إلى المعدّل } } private int _cadence; protected virtual int Gear // يمكن استدعاءه فقط من هذا الصنف أو الأصناف المتفرّعة منه Protected:محميّ { get; // تُنشأ خاصيّة تلقائية بحيث لا تحتاج لإضافة حقل بيانات set; } internal int Wheels // داخليّ Internal: يُمكن الوصول إليه من نفس الملف التنفيذي { get; private set; // يمكن تغيير مجال المسترجعات والمعدّلات } int _speed; // ولا يمكن الوصول إليها إلا من داخل الصنف Private كل الخاصيّات هي مبدئيا خاصّة // يمكن أيضا استخدام الكلمة المفتاحية private public string Name { get; set; }
للخاصيّات صياغة استثنائية عندما نريد خاصية متاحة للقراءة فقط بمعنى أنها تعيد فقط نتيجة عبارة
public string LongName => Name + " " + _speed + " speed";
النوع enum
هو نوع بيانات قيمية يتمثّل في مجموعة من المتغيّرات ثابتة القيمة
هذا النوع هو في الواقع مجرّد ربط اسم بقيمة (عددية، إن لم يحدد نوع آخر)
أنواع البيانات الموثوقة في قيم الثوابت هي byte
, sbyte
, short
, ushort
, int
, uint
, long
, و ulong
ولا يمكن أن توجد نفس القيمة مرتين في متغيّر من النوع enum
public enum BikeBrand { AIST, BMC, Electra = 42, // مباشرة enum يمكن تعيين قيمة المتغيّر في Gitane // 43 }
عرّفنا هذا النوع داخل الصنف Bicycle
لذا فهو نوع داخلي وعندما نريد استخدامه خارج الصنف فسيتوجّب أن نكتُب Bicycle.Brand
public BikeBrand Brand;
بعد تعريف نوع enum
يصبح بإمكاننا تعريف متغيّر من هذا النوع
تستطيع التعليم على وجود قيم عدّة يمكن الاختيار بينها بإضافة الصنف FlagsAttribute
قبل تعريف النوع enum
يمكن استخدام أي صنف متفرّع عن الصنف Attribute
لتعليم أنواع البيانات، التوابع والمعاملات…إلخ
يمكن استخدام العوامل المنطقية &
و |
لإجراء عمليّات منطقية داخل القيمة
[Flags] public enum BikeAccessories { None = 0, Bell = 1, MudGuards = 2, // نحتاج لتعيين القيم يدويا Racks = 4, Lights = 8, FullPackage = Bell | MudGuards | Racks | Lights }
الاستخدام: aBike.Accessories.HasFlag(Bicycle.BikeAccessories.Bell)
في الإصدارات السابقة على الإصدار الرابع من إطار العمل NET
(aBike.Accessories & Bicycle.BikeAccessories.Bell) == Bicycle.BikeAccessories.Bell
public BikeAccessories Accessories { get; set; }
تنتمي الخاصيّات المُعلمة بالكلمة المفتاحية static
للصنف نفسه، وليس لكائن عكس بقية الخاصيّات
يمكن الوصول إلى هذه الخاصيّات دون الرجوع إلى كائن محدّد
;Console.WriteLine("Bicycles created: " + Bicycle.bicyclesCreated)
public static int BicyclesCreated { get; set; }
عيّن القيم غير القابلة للتعديل أثناء التشغيل ولا يمكن إسناده إلا عند تعريفها أو داخل مشيّد
readonly bool _hasCardsInSpokes = false; // خاصيّة خاصّة وللقراءة فقط
المشيّدات Constructors هي طريقة لإنشاء الأصناف
أدناه المشيّد المبدئي
public Bicycle() { this.Gear = 1; // يمكن الوصول إلى خاصيّات الصنف بالكلمة المفتاحية this Cadence = 50; // إلا أنك لا تحتاجها في كل الحالات _speed = 5; Name = "Bontrager"; Brand = BikeBrand.AIST; BicyclesCreated++; }
هذا مشيّد مُعيّن (يحوي معطيات)
public Bicycle(int startCadence, int startSpeed, int startGear, string name, bool hasCardsInSpokes, BikeBrand brand) : base() // أولا base يستدعي { Gear = startGear; Cadence = startCadence; _speed = startSpeed; Name = name; _hasCardsInSpokes = hasCardsInSpokes; Brand = brand; }
يمكن وضع المشيّدات بالتسلسل
public Bicycle(int startCadence, int startSpeed, BikeBrand brand) : this(startCadence, startSpeed, 0, "big wheels", true, brand) { }
صيغة كتابة الدوال
(<public/private/protected> <return type> <function name> <args>)
يمكن للأصناف أن تعيّن مسترجعات ومعدّلات لحقولها كما يمكنها تنفيذ الخاصيّات عبر دوال (وهي الطريقة المفضّلة في Csharp) ويمكن لمعاملات التابع أن تحوي قيما مبدئية، في هذه الحالة، يمكن استدعاء التوابع بدون تمرير معطيات عن هذه العوامل
public void SpeedUp(int increment = 1) { _speed += increment; } public void SlowDown(int decrement = 1) { _speed -= decrement; }
تعيّن الخاصيّات القيم وتسترجعها. إن كان غرضك الوصول إلى البيانات فقط دون تعديلها فالخاصيّات أنسب. يمكن أن يكون للخاصيّة مسترجع أو معدّل أو كلاهما
private bool _hasTassles; // متغيّر خاص public bool HasTassles // مسترجع عام { get { return _hasTassles; } set { _hasTassles = value; } }
كما يمكنك تعريف خاصيّة تلقائية في سطر واحد
ستُنشئ هذه الصيغة حقلا داعما تلقائيا
يمكنك الحد من مجال الرؤية على المسترجع أو المعدّل أو كليهما
public bool IsBroken { get; private set; }
يمكن للخاصيّات أن تكون تلقائية التنفيذ
public int FrameSize { get; // يمكنك الحد من مجال الرؤية على المسترجع أو المعدّل //Framesize يمكنه استدعاء معدّل Bicycle يعني هذا أن الصنف private set; }
يمكن تعريف فهرس على الكائنات
يمكنك مثلا كتابة bicycle[0]
التي ترجع القيمة “chris” للحصول على أول راكب
أو كتابة “bicycle[1] = "lisa
لتعيين الراكب الثاني (دراجة رباعية المقاعد!)
private string[] passengers = { "chris", "phil", "darren", "regina" }; public string this[int i] { get { return passengers[i]; } set { passengers[i] = value; } }
تابع لعرض قيم حقول الكائن
public virtual string Info() { return "Gear: " + Gear + " Cadence: " + Cadence + " Speed: " + _speed + " Name: " + Name + " Cards in Spokes: " + (_hasCardsInSpokes ? "yes" : "no") + "n------------------------------n" ; }
يمكن للتوابع أن تكون ثابتة (الكلمة المفتاحية static
). مناسبة للدوال المساعدة
public static bool DidWeCreateEnoughBicycles() {
داخل التابع الثابت لا يمكن إجراء عمليات سوى على الحقول الثابتة
return BicyclesCreated > 9000; }
إن كان الصنف لا يحتاج إلا إلى حقول ثابتة فربما يكون من الأفضل أن يكون الصنف نفسه ثابتا
} // نهاية الصنف Bicycle
PennyFarthing
هو صنف متفرّع من الصنف Bicycle
class PennyFarthing : Bicycle {
(يمثّل هذا الصنف تلك الدراجات الهوائية التي لديها عجلة أمامية كبيرة جدا، وليست لديها مسنّنات Gears
لتعديل السرعة) . نستدعي مشيّد الصنف الأب
public PennyFarthing(int startCadence, int startSpeed) : base(startCadence, startSpeed, 0, "PennyFarthing", true, BikeBrand.Electra) { } protected override int Gear { get { return 0; } set { throw new InvalidOperationException("You can't change gears on a PennyFarthing"); } } public static PennyFarthing CreateWithGears(int gears) { var penny = new PennyFarthing(1, 1); // عمليا لا توجد دراجة من نوع PennyFarthing بمسنّنات penny.Gear = gears; return penny; } public override string Info() { string result = "PennyFarthing bicycle "; result += base.ToString(); // نستدعي التابع الأصلي الموجود في الصنف الأب return result; } }
تحتوي الواجهات على التوقيعات فقط
interface IJumpable { void Jump(int meters); // جميع الأعضاء في الواجهة هي مبدئيا عمومية } interface IBreakable { bool Broken { get; } // يمكن للواجهات أن تحوي خاصيّات كما يمكنها أن تتضمّن واجهات وأحداثا }
يمكن للأصناف أن ترث Inherit
من صنف واحد آخر على الأكثر، إلا أنه يمكنها تنفيذ أي عدد من الواجهات ويجب أن يكون الصنف الأب الأول في لائحة الأصناف تليه الواجهات كلّها
class MountainBike : Bicycle, IJumpable, IBreakable { int damage = 0; public void Jump(int meters) { damage += meters; } public bool Broken { get { return damage > 100; } } }
صنف للاتصال بقاعدة البيانات، نستخدمه مثالا لعمل LinqToSql
يعمل إطار العمل EntityFramework Code First لربط الكائنات بسجلات جداول البيانات (بنفس طريقة ActiveRecord في روبي، إلا أنه ثنائي الاتجاه)
public class BikeRepository : DbContext { public BikeRepository() : base() { } public DbSet<Bicycle> Bikes { get; set; } }
يمكن تقسيم الأصناف على ملفات cs.
عدّة
A1.cs
public partial class A { public static void A1() { Console.WriteLine("Method A1 in class A"); } }
A2.cs
public partial class A { public static void A2() { Console.WriteLine("Method A2 in class A"); } }
يستخدم الصنف Program
أدناه الصنف A
المُقسّم على ملفي cs.
Program using the partial class "A" public class Program { static void Main() { A.A1(); A.A2(); } }
يمكن الإداراج في سلاسل المحارف String interpolation بكتابة $
أمام السلسلة ثم إحاطة المتغيّر المُدرج بقوسين معكوفين { }
. يمكنك أيضا تجميع السلسلتين، الأصلية والمُعدّلة، بالعلامة @
public class Rectangle { public int Length { get; set; } public int Width { get; set; } } class Program { static void Main(string[] args) { Rectangle rect = new Rectangle { Length = 5, Width = 3 }; Console.WriteLine($"The length is {rect.Length} and the width is {rect.Width}"); string username = "User"; Console.WriteLine([email protected]"C:Users{username}Desktop"); } }
ميزات جديدة في الإصدار C# 6
class GlassBall : IJumpable, IBreakable {
تمهيد الخاصيّات التلقائية
public int Damage { get; private set; } = 0;
تمهيد الخاصيّات التلقائية المقتصرة على المسترجعات
public string Name { get; } = "Glass ball";
تمهيد الخاصيّات التلقائية المقتصرة على المسترجعات في المشيّد
public string GenieName { get; } public GlassBall(string genieName = null) { GenieName = genieName; } public void Jump(int meters) { if (meters < 0)
العبارة nameof()
مستحدثة وينتُج عنها التحقّق من وجود المعرّف
"nameof(x) == "x
تحول على سبيل المثال دون بقاء أسماء المتغيّرات القديمة في رسائل الخطأ بعد تحديثها
throw new ArgumentException("Cannot jump negative amount!", nameof(meters)); Damage += meters; }
الخاصيّات المعرَّفة ضمن هيكل العبارة
public bool Broken => Damage > 100;
نفس الشيء بالنسبة للتوابع
public override string ToString() // سلسلة محارف تُدرج ضمنها متغيّرات => $"{Name}. Damage taken: {Damage}"; public string SummonGenie()
العوامل المشترطة بالقيمة الفارغة null
ترجع العبارة x?.y
القيمة null
بمجرد كون x
مساوية ل null
، بدون تقييم y
=> GenieName?.ToUpper(); } static class MagicService { private static bool LogException(Exception ex) { /* سجّل الاستثناءات في مكان ما */ log exception somewhere */ return false; } public static bool CastSpell(string spell) { try { // API نفترض هنا أننا نستدعي واجهة تطبيقات برمجية throw new MagicServiceException("Spell failed", 42); // نجح الاستدعاء return true; }
يلتقط استثناء في حالة إخفاق استدعاء واجهة التطبيقات، أي أن قيمة Code
تساوي 42
Only catch if Code is 42 i.e. spell failed catch(MagicServiceException ex) when (ex.Code == 42) { // أخفق الاستدعاء return false; }
استثماءات أخرى أو الاستثناء MagicServiceException
عندما تكون قيمة المتغير Code
مختلفة عن 42
catch(Exception ex) when (LogException(ex)) { // لا يصل التنفيذ إلى هذه الكتلة } return false; }
لاحظ أن التقاط الاستثناء MagicServiceException
وإعادة إطلاقه عندما يكون المتغير Code
لا يساوي القيمة 42 أو 117 هو أمر مختلف، إذ أن كتلة catch-all
الأخيرة لن تلتقط الاستثناء المُعاد
public class MagicServiceException : Exception { public int Code { get; } public MagicServiceException(string message, int code) : base(message) { Code = code; } }
الخاصية Obsolete
public static class PragmaWarning { [Obsolete("Use NewMethod instead", false)] public static void ObsoleteMethod() { /*شفرة برمجية قديمة هنا */ } public static void NewMethod() { /* شفرة برمجية جديدة */ } public static void Main() { ObsoleteMethod();
تحذير يظهر عند استخدام شفرة برمجية قديمة، ناتج عن الوسم Obsolete
أعلاه
CS0618: 'ObsoleteMethod is obsolete: Use NewMethod instead'
تعطّل التعليمة التالية إظهار التحذير السابق
#pragma warning disable CS0618 ObsoleteMethod(); // لا تحذير #pragma warning restore CS0618 ObsoleteMethod(); // CS0618: 'ObsoleteMethod is obsolete: Use NewMethod instead' } } } // نهاية فضاء الأسماء
using System;
ميزة في C# 6: إمكانية استخدام static
مع using
using static System.Math; namespace Learning.More.CSharp { class StaticUsing { static void Main() { // using مع static بدون استخدام Console.WriteLine("The square root of 4 is {}.", Math.Sqrt(4)); // using مع static باستخدام Console.WriteLine("The square root of 4 is {}.", Sqrt(4)); } } }
ميزة جديدة في C# 7
ثبّت آخر إصدار من Microsoft.Net.Compilers باستخدام Nuget
ثبّت آخر إصدار من System.ValueTuple باستخدام Nuget
using System; namespace Csharp7 {
الأزواج المرتبة Tuples
، التفكيك DECONSTRUCTION
والإلغاءات Discards
class TuplesTest { public (string, string) GetName() { // Item1، Item2 .... تُسمى الحقول في الأزواج المرتبة مبدئيا بـ var names1 = ("Peter", "Parker"); Console.WriteLine(names1.Item2); // => Parker
يمكن تخصيص أسماء الحقول
تعريف النوع الأول
(string FirstName, string LastName) names2 = ("Peter", "Parker");
تعريف النوع الثاني
var names3 = (First:"Peter", Last:"Parker"); Console.WriteLine(names2.FirstName); // => Peter Console.WriteLine(names3.Last); // => Parker return names3; } public string GetLastName() { var fullName = GetName();
يمكن تفكيك الأزواج المرتبة
(string firstName, string lastName) = fullName;
يمكن إلغاء حقول من الزوج المرتب بعد تفكيكه بالعلامة _
Fields in a deconstructed tuple can be discarded by using _ var (_, last) = fullName; return last; }
يمكن تفكيك أي نوع بيانات على نفس المنوال باستخدام التابع Deconstruct
public int randomNumber = 4; public int anotherRandomNumber = 10; public void Deconstruct(out int randomNumber, out int anotherRandomNumber) { randomNumber = this.randomNumber; anotherRandomNumber = this.anotherRandomNumber; } static void Main(string[] args) { var tt = new TuplesTest(); (int num1, int num2) = tt; Console.WriteLine($"num1: {num1}, num2: {num2}"); // => num1: 4, num2: 10 Console.WriteLine(tt.GetLastName()); } }
مطابقة الأنماط Pattern matching
class PatternMatchingTest { public static (string, int)? CreateLogMessage(object data) { switch(data) { // when ترشيح إضافي باستخدام case System.Net.Http.HttpRequestException h when h.Message.Contains("404"): return (h.Message, 404); case System.Net.Http.HttpRequestException h when h.Message.Contains("400"): return (h.Message, 400); case Exception e: return (e.Message, 500); case string s: return (s, s.Contains("Error") ? 500 : 200); case null: return null; default: return (data.ToString(), 500); } } }
الإحالة إلى الموارد المحلية Reference locals
تعطيك إمكانية إرجاع مرجع Reference كائن بدلا من قيمته
class RefLocalsTest {
لاحظ الكلمة المفتاحية ref في تعليمة الإرجاع return
public static ref string FindItem(string[] arr, string el) { for(int i=0; i<arr.Length; i++) { if(arr[i] == el) { // إرجاع المرجع return ref arr[i]; } } throw new Exception("Item not found"); } public static void SomeMethod() { string[] arr = {"this", "is", "an", "array"}; //في كل مكان ref لاحظ ref string item = ref FindItem(arr, "array"); item = "apple"; Console.WriteLine(arr[3]); // => apple } }
الدوال المحليّة Local functions
class LocalFunctionTest { private static int _id = 0; public int id; public LocalFunctionTest() { id = generateId();
لا يمكن الوصول إلى الدالة المحلية خارج هذا المجال
int generateId() { return _id++; } } public static void AnotherMethod() { var lf1 = new LocalFunctionTest(); var lf2 = new LocalFunctionTest(); Console.WriteLine($"{lf1.id}, {lf2.id}"); // => 0, 1 int id = generateId(); // خطأ // error CS0103: The name 'generateId' does not exist in the current context } } }
ترجمة -وبتصرّف- للمقال Learn C# in Y Minutes