الخصائص من أجل التغليف
جزء من قسم Object Oriented Programming في رحلة C# على Coddy — الدرس 32 من 70.
تعد الخصائص (Properties) الأداة الأساسية لتنفيذ التغليف (encapsulation) في لغة C#. فبدلاً من كشف الحقول (fields) مباشرة، تقوم بتغليفها بخصائص تتحكم في كيفية الوصول إلى البيانات وتعديلها.
يجمع هذا النمط بين حقل خاص وخاصية عامة تتضمن التحقق من الصحة أو منطقاً معيناً في أدوات الوصول (accessors) الخاصة بها:
public class Temperature
{
private double celsius;
public double Celsius
{
get { return celsius; }
set
{
if (value < -273.15)
celsius = -273.15; // الحد الأدنى للصفر المطلق
else
celsius = value;
}
}
}الآن لا يمكن للكود الخارجي تعيين درجة حرارة مستحيلة. تعمل الخاصية كحارس بوابة، مما يضمن بقاء الكائن الخاص بك دائماً في حالة صالحة. هذا هو مفهوم التغليف (encapsulation) قيد التنفيذ - حيث تكون البيانات الداخلية محمية مع بقائها قابلة للوصول من خلال واجهة محكومة.
يمكنك أيضاً استخدام الخصائص لحساب القيم أو إحداث تأثيرات جانبية:
public double Fahrenheit
{
get { return celsius * 9 / 5 + 32; }
set { celsius = (value - 32) * 5 / 9; }
}يرى العالم الخارجي خاصيتين، ولكن داخلياً يوجد حقل واحد فقط. تتيح لك الخصائص تغيير تفاصيل التنفيذ دون كسر الكود الذي يستخدم فئتك (class) - وهي ميزة رئيسية للتغليف (encapsulation) السليم.
التحدي
سهللنقم ببناء نظام مخزون منتجات يستخدم الخصائص (properties) لحماية سلامة البيانات. ستقوم بإنشاء فئة (class) حيث لا يمكن للأسعار أن تكون سالبة أبداً، ويتم الاحتفاظ بالكميات دائماً ضمن حدود صالحة - مما يوضح كيف تعمل الخصائص كحراس لبياناتك.
ستقوم بتنظيم الكود الخاص بك عبر ملفين:
Product.cs: أنشئ فئةProductداخل مساحة الأسماءInventoryالتي تدير معلومات المنتج مع التحقق من الصحة من خلال الخصائص. يحتاج منتجك إلى:- حقل خاص
price(decimal) مع خاصية عامةPriceتضمن عدم انخفاض السعر أبداً عن0- إذا حاول شخص ما تعيين سعر سالب، فيجب تخزينه كـ0بدلاً من ذلك - حقل خاص
quantity(int) مع خاصية عامةQuantityتبقي القيم بين0و1000- القيم الأقل من 0 تصبح 0، والقيم الأعلى من 1000 تصبح 1000 - خاصية عامة تلقائية التنفيذ (auto-implemented property) باسم
Name(string) - خاصية محسوبة للقراءة فقط
TotalValueتعيدPrice * Quantity
- حقل خاص
Program.cs: في ملفك الرئيسي، أنشئProductباستخدام قيم الإدخال. ثم حاول تعديل السعر والكمية بقيم إدخال إضافية قد تكون غير صالحة. اطبع حالة المنتج بعد الإنشاء وبعد كل تعديل لترى كيف تحمي الخصائص البيانات.
ستتلقى خمسة مدخلات:
- اسم المنتج
- السعر الأولي (decimal)
- الكمية الأولية (integer)
- السعر الجديد المراد تعيينه (decimal، قد يكون سالباً)
- الكمية الجديدة المراد تعيينها (integer، قد تكون خارج النطاق)
اطبع المخرجات بهذا التنسيق:
After creation:
{Name}: Price={Price}, Qty={Quantity}, Total={TotalValue}
After modifications:
{Name}: Price={Price}, Qty={Quantity}, Total={TotalValue}على سبيل المثال، إذا كانت المدخلات هي Laptop، و 999.99، و 50، و -100، و 1500، فيجب أن تكون المخرجات:
After creation:
Laptop: Price=999.99, Qty=50, Total=49999.50
After modifications:
Laptop: Price=0, Qty=1000, Total=0لاحظ كيف أصبح السعر السالب 0 وتم تقييد الكمية 1500 لتصبح 1000. خصائصك تحمي الكائن من الحالات غير الصالحة - هذا هو مفهوم التغليف (encapsulation) في العمل!
ورقة مرجعية
تغلف الخصائص الحقول الخاصة مع وصول متحكم فيه من خلال موصلات get و set، مما يتيح التحقق من الصحة والكبسلة:
public class Temperature
{
private double celsius;
public double Celsius
{
get { return celsius; }
set
{
if (value < -273.15)
celsius = -273.15;
else
celsius = value;
}
}
}يمكن للخصائص حساب القيم بناءً على الحقول الداخلية:
public double Fahrenheit
{
get { return celsius * 9 / 5 + 32; }
set { celsius = (value - 32) * 5 / 9; }
}تتجاهل الخصائص المخصصة للقراءة فقط موصل set، مما يسمح فقط باسترجاع القيم المحسوبة أو المخزنة.
جرّب بنفسك
using System;
using Inventory;
class Program
{
public static void Main(string[] args)
{
// قراءة المدخلات
string name = Console.ReadLine();
decimal initialPrice = Convert.ToDecimal(Console.ReadLine());
int initialQuantity = Convert.ToInt32(Console.ReadLine());
decimal newPrice = Convert.ToDecimal(Console.ReadLine());
int newQuantity = Convert.ToInt32(Console.ReadLine());
// TODO: إنشاء منتج (Product) باستخدام القيم الأولية
// TODO: طباعة حالة المنتج بعد الإنشاء
Console.WriteLine("After creation:");
// تنسيق الطباعة: {Name}: Price={Price}, Qty={Quantity}, Total={TotalValue}
// TODO: تعديل السعر والكمية بالقيم الجديدة
// TODO: طباعة حالة المنتج بعد التعديلات
Console.WriteLine("After modifications:");
// تنسيق الطباعة: {Name}: Price={Price}, Qty={Quantity}, Total={TotalValue}
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةNamespaces والتوجيهاتمقدمة في Classes و Objectsالكلمة المفتاحية 'this'Methods والمعاملاتFields مقابل PropertiesConstructorsObject Initializersمراجعة - آلة حاسبة بسيطة4الوراثة
صيغة الوراثة الأساسية (:)الكلمة المفتاحية 'base'الكلمات المفتاحية Virtual و Overrideالأصناف المختومة (Sealed Classes)الفئة الأساسية 'object'مراجعة - هيكلية الموظفين7ميزات متقدمة
تحميل العوامل (Operator Overloading)المفهرسات (Indexers (this[]))إعادة تعريف ()ToStringدوال التوسعة (Extension Methods)ملخص - Custom List2الخصائص والأعضاء الساكنة (Static Members)
الخصائص المطبقة تلقائيًا (Auto-Implemented Properties)خصائص القراءة فقط والكتابة فقطالحقول والدوال الساكنة (Static)الفئات الساكنة (Static Classes)الأعضاء ذات البنية التعبيرية (Expression-Bodied Members)5تعدد الأشكال والواجهات (Polymorphism & Interfaces)
تعدد الأشكال: وقت التجميع مقابل وقت التشغيلالواجهة مقابل الفئة المجردة (Abstract Class)الواجهات المتعددةالواجهات الصريحةUpcasting و Downcastingملخص - حاسبة الأشكال8مفاهيم البرمجة كائنية التوجه (OOP) المتقدمة
التركيب بدلاً من الوراثةالـ Generics (الأصناف والدوال)الـ Delegates والأحداثالـ Attributes والـ Reflectionواجهة IDisposable وجملة usingأساسيات Dependency Injection