الحقول الخلفية (Backing Fields)
جزء من قسم Object Oriented Programming في رحلة C# على Coddy — الدرس 17 من 70.
تعد الخصائص المنفذة تلقائيًا مريحة، ولكنك تحتاج أحيانًا إلى مزيد من التحكم في كيفية تخزين الخاصية لقيمتها أو استرجاعها. وهنا يأتي دور الحقول الخلفية (backing fields). الحقل الخلفي هو حقل خاص يخزن البيانات الفعلية للخاصية.
public class Product
{
private decimal _price; // حقل خلفي
public decimal Price
{
get { return _price; }
set { _price = value; }
}
}يبدو هذا كعمل إضافي مقارنة بالخصائص التلقائية (auto-properties)، ولكن الحقول الداعمة (backing fields) تتيح لك إضافة منطق برمجِي. على سبيل المثال، يمكنك التحقق من صحة البيانات قبل تخزينها:
public class Product
{
private decimal _price;
public decimal Price
{
get => _price;
set => _price = value < 0 ? 0 : value;
}
}الآن تصبح الأسعار السالبة صفراً تلقائياً. يمكنك أيضاً تحويل البيانات عند القراءة أو الكتابة، أو تسجيل التغييرات، أو تشغيل إجراءات أخرى.
تستخدم اصطلاحات تسمية الحقل الداعم عادةً بادئة بشرطة سفلية (_price) أو أحرفاً صغيرة (price) لتمييزه عن الخاصية.
عندما تستخدم الخصائص المنفذة تلقائيًا مثل public decimal Price { get; set; }، يقوم المترجم في الواقع بإنشاء حقل دعم مخفي لك. تمنحك حقول الدعم الصريحة نفس التخزين مع تحكم كامل في السلوك.
التحدي
سهلدعونا نبني نظام مستشعر درجة حرارة يستخدم حقولاً خلفية (backing fields) للتحقق من البيانات وتحويلها أثناء تخزينها واسترجاعها.
ستقوم بإنشاء ملفين لتنظيم الكود الخاص بك:
TemperatureSensor.cs: قم بتعريف فئةTemperatureSensorداخل مساحة الأسماءSensors. يقيس هذا المستشعر درجة الحرارة بالدرجة المئوية، ولكننا نحتاج إلى التأكد من بقاء القراءات ضمن حدود صالحة. استخدم الحقول الخلفية لتنفيذ ما يلي:- خاصية
Location(سلسلة نصية) مع حقل خلفي_location. عند تعيين الموقع، إذا كانت القيمة فارغة أو null، قم بتخزين"Unknown"بدلاً من ذلك. - خاصية
Temperature(double) مع حقل خلفي_temperature. عند تعيين درجة الحرارة، قم بحصر القيمة بين-50و150(إذا كانت أقل من -50، خزن -50؛ وإذا كانت أعلى من 150، خزن 150). - خاصية للقراءة فقط
TemperatureFahrenheitتقوم بحساب وإرجاع درجة الحرارة بالفهرنهايت باستخدام الصيغة:(celsius * 9 / 5) + 32. يجب أن تستخدم هذه الخاصية الحقل الخلفي مباشرة. - منشئ (constructor) يقبل قيم الموقع ودرجة الحرارة الأولية.
- خاصية
Program.cs: في ملفك الرئيسي، قم بإنشاء مستشعر باستخدام قيم الإدخال واعرض قراءاته. ثم قم بتحديث درجة الحرارة بقيمة إدخال ثانية واعرض القراءات المحدثة.
ستتلقى ثلاثة مدخلات:
- موقع المستشعر
- قراءة درجة الحرارة الأولية
- قراءة درجة الحرارة المحدثة
اطبع المخرجات بهذا التنسيق:
Sensor: {Location}
Temperature: {Temperature}C ({TemperatureFahrenheit}F)
Updated Temperature: {Temperature}C ({TemperatureFahrenheit}F)على سبيل المثال، إذا كانت المدخلات هي Lab Room و 25 و 200، يجب أن تكون المخرجات:
Sensor: Lab Room
Temperature: 25C (77F)
Updated Temperature: 150C (302F)لاحظ كيف تم حصر القراءة الثانية 200 لتصبح 150 لأنها تتجاوز الحد الأقصى لدرجة الحرارة الصالحة. تمنحك الحقول الخلفية هذا التحكم في كيفية تخزين البيانات!
ورقة مرجعية
الـ backing field (الحقل الخلفي) هو حقل خاص (private field) يخزن البيانات الفعلية لخاصية ما، مما يسمح لك بإضافة منطق مخصص لجلب القيم وتعيينها.
الصيغة الأساسية للحقل الخلفي
public class Product
{
private decimal _price; // حقل خلفي
public decimal Price
{
get { return _price; }
set { _price = value; }
}
}صيغة التعبير المختصرة (Expression-Bodied Syntax)
public decimal Price
{
get => _price;
set => _price = value;
}إضافة منطق التحقق من الصحة
تسمح لك الحقول الخلفية بالتحقق من صحة البيانات أو تحويلها قبل تخزينها:
private decimal _price;
public decimal Price
{
get => _price;
set => _price = value < 0 ? 0 : value; // منع الأسعار السالبة
}اصطلاح التسمية
عادةً ما تستخدم الحقول الخلفية بادئة بشرطة سفلية (_price) أو أحرفًا صغيرة (price) لتمييزها عن اسم الخاصية.
الخصائص التلقائية مقابل الحقول الخلفية
الخصائص المنفذة تلقائيًا مثل public decimal Price { get; set; } تنشئ تلقائيًا حقلاً خلفيًا مخفيًا. بينما تمنحك الحقول الخلفية الصريحة نفس التخزين مع تحكم كامل في السلوك.
جرّب بنفسك
using System;
using Sensors;
class Program
{
public static void Main(string[] args)
{
// قراءة المدخلات
string location = Console.ReadLine();
double initialTemp = Convert.ToDouble(Console.ReadLine());
double updatedTemp = Convert.ToDouble(Console.ReadLine());
// TODO: إنشاء TemperatureSensor مع الموقع ودرجة الحرارة الأولية
// TODO: طباعة موقع المستشعر وقراءات درجة الحرارة الأولية
// التنسيق: "Sensor: {Location}"
// التنسيق: "Temperature: {Temperature}C ({TemperatureFahrenheit}F)"
// TODO: تحديث درجة حرارة المستشعر بالقيمة المحدثة
// TODO: طباعة قراءات درجة الحرارة المحدثة
// التنسيق: "Updated Temperature: {Temperature}C ({TemperatureFahrenheit}F)"
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس 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 Injection3بنية الكلاسات
بيانات الـ Instance مقابل البيانات الـ Staticالكلمات المفتاحية 'readonly' و 'const'الحقول الخلفية (Backing Fields)مراجعة - مدير الحساب البنكي