فحص الخصائص التي تم تنفيذها تلقائيا

مكتمل

يتضمن بناء الجملة القياسي لتعريف خاصية في C# موصل get وملحق set. ومع ذلك، عندما لا يكون هناك منطق آخر مطلوب في الملحقات، يمكنك استخدام الخصائص المنفذة تلقائيا.

الخصائص التي تم تنفيذها تلقائيا

تجعل الخصائص المنفذة تلقائيا تعريف الخاصية أكثر إيجازا عندما لا يكون هناك منطق آخر مطلوب في ملحقات الخاصية. كما أنها تمكن التعليمات البرمجية للعميل لإنشاء كائنات. عند تعريف خاصية كما هو موضح في المثال التالي، يقوم المحول البرمجي بإنشاء حقل دعم خاص مجهول لا يمكن الوصول إليه إلا من خلال get الخاصية وملحقات set. يمكن أيضا الإعلان عن init الملحقات كخصائص يتم تنفيذها تلقائيا.

يوضح المثال التالي فئة بسيطة تحتوي على بعض الخصائص التي تم تنفيذها تلقائيا:


// This class is mutable. Its data can be modified from
// outside the class.
public class Customer
{
    // Auto-implemented properties for trivial get and set
    public double TotalPurchases { get; set; }
    public string Name { get; set; }
    public int CustomerId { get; set; }

    // Constructor
    public Customer(double purchases, string name, int id)
    {
        TotalPurchases = purchases;
        Name = name;
        CustomerId = id;
    }

    // Methods
    public string GetContactInfo() { return "ContactInfo"; }
    public string GetTransactionHistory() { return "History"; }

    // .. Other methods, events, etc.
}

class Program
{
    static void Main()
    {
        // Initialize a new object.
        Customer cust1 = new Customer(4987.63, "Northwind", 90108);

        // Modify a property.
        cust1.TotalPurchases += 499.99;
    }
}

تقوم الخصائص التي يتم تنفيذها تلقائيا والخصائص المدعومة بالحقل بتعريف حقل دعم مثيل خاص. يمكنك تهيئة الخصائص التي تم تنفيذها تلقائيا على غرار الحقول:


public string FirstName { get; set; } = "FirstName";

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

  • قم بتعريف الحصول على ملحق فقط (غير قابل للتغيير في كل مكان باستثناء الدالة الإنشائية).
  • أعلن عن الحصول على ملحق وملحقات init (غير قابل للتغيير في كل مكان باستثناء أثناء بناء الكائن).
  • قم بتعريف ملحق المجموعة على أنه خاص (غير قابل للتغيير للمستهلكين).

قد تحتاج إلى إضافة التحقق من الصحة إلى خاصية تم تنفيذها تلقائيا. يضيف C# 13 خصائص الحقل المدعومة كميزة معاينة. يمكنك استخدام الكلمة الأساسية للحقل للوصول إلى حقل النسخ الاحتياطي المجمع للمحول البرمجي لخاصية تم تنفيذها تلقائيا. على سبيل المثال، يمكنك التأكد من أنه لا يمكن تعيين الخاصية FirstName في المثال السابق إلى null أو السلسلة الفارغة:


public string FirstName 
{ 
    get; 
    set 
    { 
        field = (string.IsNullOrWhiteSpace(value) is false
            ? value
            : throw new ArgumentException(nameof(value), "First name can't be whitespace or null"));
    }
} = "FirstName";

تمكنك هذه الميزة من إضافة منطق إلى الملحقات دون مطالبتك بتعريف حقل الدعم بشكل صريح. يمكنك استخدام الكلمة الأساسية للحقل للوصول إلى حقل النسخ الاحتياطي الذي تم إنشاؤه بواسطة المحول البرمجي.

مهم

الكلمة الأساسية field هي ميزة معاينة في C# 13. يجب استخدام .NET 9 وتعيين عنصر <LangVersion> إلى preview في ملف المشروع لاستخدام الكلمة الأساسية السياقية field.

يجب أن تكون حذرا باستخدام ميزة الكلمة الأساسية field في فئة تحتوي على حقل يسمى field. ظلال الكلمة الأساسية field الجديدة لحقل يسمى field في نطاق ملحق الخاصية. يمكنك إما تغيير اسم متغير field، أو استخدام الرمز المميز @ للإشارة إلى معرف field على أنه @field.

خصائص مع حقول دعم

يمكنك خلط مفهوم الخاصية المحسوبة مع حقل خاص وإنشاء خاصية تم تقييمها مؤقتا . على سبيل المثال، قم بتحديث الخاصية FullName بحيث يحدث تنسيق السلسلة في الوصول الأول:


public class Person
{
    public Person() { }

    [SetsRequiredMembers]
    public Person(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }

    public required string FirstName { get; init; }
    public required string LastName { get; init; }

    private string? _fullName;
    public string FullName
    {
        get
        {
            if (_fullName is null)
                _fullName = $"{FirstName} {LastName}";
            return _fullName;
        }
    }
}

يعمل هذا التنفيذ لأن خصائص FirstNameLastName للقراءة فقط. يمكن للناس تغيير أسمائهم. يتطلب تحديث خصائص FirstNameLastName للسماح بملحقات set إبطال أي قيمة مخزنة مؤقتا fullName. تقوم بتعديل set الملحقات للخاصية FirstName و LastName بحيث يتم حساب الحقل fullName مرة أخرى:


public class Person
{
    private string? _firstName;
    public string? FirstName
    {
        get => _firstName;
        set
        {
            _firstName = value;
            _fullName = null;
        }
    }

    private string? _lastName;
    public string? LastName
    {
        get => _lastName;
        set
        {
            _lastName = value;
            _fullName = null;
        }
    }

    private string? _fullName;
    public string FullName
    {
        get
        {
            if (_fullName is null)
                _fullName = $"{FirstName} {LastName}";
            return _fullName;
        }
    }
}

يقيم هذا الإصدار النهائي خاصية FullName فقط عند الحاجة. يتم استخدام الإصدار المحسوب مسبقا إذا كان صالحا. وإلا، يقوم الحساب بتحديث القيمة المخزنة مؤقتا. لا يحتاج المطورون الذين يستخدمون هذه الفئة إلى معرفة تفاصيل التنفيذ. لا تؤثر أي من هذه التغييرات الداخلية على استخدام الكائن Person.

بدءا من C# 13، يمكنك إنشاء partial properties في فئات جزئية. لا يمكن أن يكون إعلان التنفيذ لخاصية partial خاصية يتم تنفيذها تلقائيا. تستخدم الخاصية التي يتم تنفيذها تلقائيا نفس بناء الجملة كتصريح تعريف خاصية جزئية.

تنفيذ فئة خفيفة الوزن مع خصائص يتم تنفيذها تلقائيا

قد تواجه مواقف عندما تحتاج إلى إنشاء فئة خفيفة الوزن غير قابلة للتغيير تعمل فقط على تغليف مجموعة من الخصائص التي تم تنفيذها تلقائيا. استخدم هذا النوع من الإنشاء بدلا من struct عندما يجب استخدام دلالات نوع المرجع.

يمكنك إنشاء خاصية غير قابلة للتغيير بالطرق التالية:

  • قم بتعريف ملحق get فقط، مما يجعل الخاصية غير قابلة للتغيير في كل مكان باستثناء الدالة الإنشائية للنوع.
  • قم بتعريف موصل init بدلا من ملحق set، مما يجعل الخاصية settable فقط في الدالة الإنشائية أو باستخدام تهيئة كائن.
  • قم بتعريف ملحق set ليكون private. يتم تعيين الخاصية ضمن النوع ، ولكنها غير قابلة للتغيير للمستهلكين.

يمكنك إضافة معدل required إلى إعلان الخاصية لإجبار المتصلين على تعيين الخاصية كجزء من تهيئة كائن جديد.

يوضح المثال التالي كيف تختلف الخاصية مع الحصول على الملحق فقط عن خاصية مع الحصول على وتعيين خاص.


class Contact
{
    public string Name { get; }
    public string Address { get; private set; }

    public Contact(string contactName, string contactAddress)
    {
        // Both properties are accessible in the constructor.
        Name = contactName;
        Address = contactAddress;
    }

    // Name isn't assignable here. This will generate a compile error.
    //public void ChangeName(string newName) => Name = newName;

    // Address is assignable here.
    public void ChangeAddress(string newAddress) => Address = newAddress;
}

يوضح المثال التالي طريقتين لتنفيذ فئة غير قابلة للتغيير باستخدام الخصائص التي تم تنفيذها تلقائيا. تعلن كل طريقة عن إحدى الخصائص ذات set خاص وواحدة من الخصائص ذات get فقط. تستخدم الفئة الأولى الدالة الإنشائية فقط لتهيئة الخصائص، وتستخدم الفئة الثانية أسلوب مصنع ثابت يستدعي الدالة الإنشائية.


// This class is immutable. After an object is created,
// it can't be modified from outside the class. It uses a
// constructor to initialize its properties.
class Contact
{
    // Read-only property.
    public string Name { get; }

    // Read-write property with a private set accessor.
    public string Address { get; private set; }

    // Public constructor.
    public Contact(string contactName, string contactAddress)
    {
        Name = contactName;
        Address = contactAddress;
    }
}

// This class is immutable. After an object is created,
// it can't be modified from outside the class. It uses a
// static method and private constructor to initialize its properties.
public class Contact2
{
    // Read-write property with a private set accessor.
    public string Name { get; private set; }

    // Read-only property.
    public string Address { get; }

    // Private constructor.
    private Contact2(string contactName, string contactAddress)
    {
        Name = contactName;
        Address = contactAddress;
    }

    // Public factory method.
    public static Contact2 CreateContact(string name, string address)
    {
        return new Contact2(name, address);
    }
}

public class Program
{
    static void Main()
    {
        // Some simple data sources.
        string[] names = ["Person One","Person Two", "Person Three",
                            "Person Four", "Person Five"];
        string[] addresses = ["123 Main St.", "345 Cypress Ave.", "678 1st Ave",
                                "12 108th St.", "89 E. 42nd St."];

        // Simple query to demonstrate object creation in select clause.
        // Create Contact objects by using a constructor.
        var query1 = from i in Enumerable.Range(0, 5)
                    select new Contact(names[i], addresses[i]);

        // List elements can't be modified by client code.
        var list = query1.ToList();
        foreach (var contact in list)
        {
            Console.WriteLine("{0}, {1}", contact.Name, contact.Address);
        }

        // Create Contact2 objects by using a static factory method.
        var query2 = from i in Enumerable.Range(0, 5)
                        select Contact2.CreateContact(names[i], addresses[i]);

        // Console output is identical to query1.
        var list2 = query2.ToList();

        // List elements can't be modified by client code.
        // CS0272:
        // list2[0].Name = "Person Six";
    }
}

/* Output:
    Person One, 123 Main St.
    Person Two, 345 Cypress Ave.
    Person Three, 678 1st Ave
    Person Four, 12 108th St.
    Person Five, 89 E. 42nd St.
*/

ينشئ المحول البرمجي حقول دعم لكل خاصية يتم تنفيذها تلقائيا. لا يمكن الوصول إلى الحقول مباشرة من التعليمات البرمجية المصدر.