TOC

This article is currently in the process of being translated into Arabic (~25% done).

صنف :

Properties

لقد ناقشنا في المقال السابق الحقول (fields). هي تشبه لحد ما المتغيرات العامة لفئة (global variables of class)، التي يمكن الوصول إليها من خلال كل الوسائل/الدوال (Methods). ناقشنا أيضا بإيجاز حقيقة أن الحقول يمكن الوصول إليها من فئات أخرى إذا تم تعينها كعامة (public)، لكن لا ينصح بهذا على العموم. ومن أجل الوصول إلى المتغيرات (variables)أو الحقول التي تريدها من خارج الفئة التي أنشأتها يجدر بك بدل ذلك استخدام الخصائص (properties).

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

الخاصية (property) تبدو قليلا مثل دمج بين الحقل و الوسيلة/الدالة، لأنها تعرف مثل الحقل مع إمكانية وصول (visibility)، نوع بيانات (data type) واسم ولكن لديها كذلك هيكل (body) مثل الوسيلة/الدالة، وهذا لأجل التحكم في السلوك.

public string Name
{
	get { return _name; }
	set { _name = value; }
}

لاحظ الكلمتين المفتاحيتين get و set ، هاتان الكلمتان تستعملان حصريا من أجل الخصائص، من أجل التحكم في السلوك أثناء القراءة (get’ing) أو التعديل (set’ing) على الحقل، يمكنك الحصول على خاصية باستعمال احدى الكلمتين get أو set فقط وذلك من أجل إنشاء خصائص للقراءة فقط أو التعديل فقط. ويمكنك حتى التحكم في إمكانية الوصول لتطبيقات الـget أو الـset، على سبيل المثال: إنشاء خاصية قابلة للقراءة من أي مكان (public) ولكن لا يتم تغييرها إلا من داخل فئتها المعرفة (private).

ستلاحظ كذلك أننا أشرنا إلى حقل يسمى _name . سوف تقوم بتعريفه كذلك في الفئة المنشأة لكي تستطيع الخاصية استعمالها. النسق المعروف لاستعمال الحقول والخصائص كالآتي:

private string _name = "John Doe";

public string Name
{
	get { return _name; }
	set { _name = value; }
}

يمكنك الآن رؤية كيف أن الحقل والخاصية يعملان معا: وسيلة/دالة القراءة get ترجع قيمة الحقل _name ، فيما أن وسيلة/دالة التعديل set ستقوم بتعيين القيمة المعطاة للحقل _name. نستعمل في وسيلة/دالة التعديل set الكلمة المفتاحية value التي تقوم في هذه الحالة الخاصة بالتعبير على القيمة المعطاة للخاصية.

So, this is pretty much as basic as it gets and at this point, we don't do anything that couldn't be accomplished with a simple public field. But at a later point, you may decide that you want to take more control of how other classes can work with the name and since you have implemented this as a property, you are free to modify the implementation without disturbing anyone using your class. For instance, the Name property could be modified to look like this:

private string _name = "John Doe";

public string Name
{
	get 
	{
		return _name.ToUpper();
	}
	set 
	{
		if(!value.Contains(" "))
			throw new Exception("Please specify both first and last name!");
		_name = value; 
	}
}

The get method now enforces that the value returned is always in UPPERCASE, no matter which case the backing field (_name) is in. In the set method, we have added a couple of lines of code to check whether the passed value contains a space, because we have decided that the name should always consist of both a first and a last name - if this is not the case, an exception is thrown. This is all very crude and simplified, but it should illustrate the full level of control you get when using properties.

Read-only properties

Most properties you'll see in the examples of this tutorial will be both readable and writeable, because that's the most common usage of properties, but it doesn't always have to be like that. First of all, you can declare a property with only a get-method, like this:

private string _name = "John Doe";

public string Name
{
	get { return _name; }
}

In this case, you can no longer change the "Name" property - you can only read it and the compiler will throw an error if you try to assign a value to it. You can still change the value of it from inside the class though, since you can simply assign a new value to the backing field "_name". Doing it that way kind of negates one of the biggest advantage to properties though: The ability to always control whether a value can be accepted. As we already talked about, the set-method is a great way to perform validation of the value, but if you assign a new value to the _name field from multiple places, because the property is read-only, you don't get this validation.

Fortunately for us, C# offers a solution to this: You can define a set method on the property, but limit its visibility, using e.g. the private or the protected keyword. This will give you the best of both worlds, where you can still assign a value to the property from inside the class (or any inherited class if you use the protected keyword) and have it validated accordingly. Here's an example:

private string _name = "John Doe";

public string Name
{
	get { return _name; }

	private set
	{
		if(IsValidName(value))
			this._name = value;
	}
}

public bool IsValidName(string name)
{
	return name.EndsWith("Doe");

}

The key difference here is simply the "private" keyword right in front of the "set" keyword, and as mentioned, you can replace it with e.g. protected or internal, depending on your needs.

Auto-implemented properties

In some cases, you don't need all the control over a field and it can feel cumbersome to implement both a field and a property with the get and set methods not doing anything besides what we saw in the first example. You may be tempted to simply declare your variable as a public field to avoid all this extra hassle. But don't do that! Fortunately for all of us, Microsoft decided to add auto-implemented properties in C# version 3, which will save you several lines of code. Just consider the difference:

Regular property with a declared backing field:

private string _name;

public string Name
{
	get { return _name; }
	set { _name = value; }
}

The exact same behavior, but with an auto-implemented property:

public string Name { get; set; }

Notice that the get and set methods are empty and that no private backing field is declared - in other words, we can now accomplish the exact same behavior as in the first example but with a single line of code! Bear in mind that the private backing field will still exist at runtime - it will be auto-implemented by the compiler, as the name implies. Should you later decide that you need more control of this specific property, you can simply change it to a regular field/property combination with the desired implementation of the get and set methods.

Notice that you are still left with an important mechanism of control from regular properties when using auto-implemented properties: You can leave out the set keyword to create a read-only property, e.g. like this:

public string ReadOnlyProperty { get; }

Write-only properties are not allowed when using auto-implemented properties.

Auto-implemented properties with default values

Prior to C# version 6, you could not define a default value for an auto-implemented property - for that, you would need a declared backing field, which would allow you to initialize the variable with a value:

private string _name = "John Doe";

public string Name
{
	get { return _name; }
	set { _name = value; }
}

But in C# version 6, Microsoft finally added the ability to initialize an auto-implemented property with a default value, like this:

public string Name { get; set; } = "John Doe";

Expression-bodied properties

Another property-related feature Microsoft implemented in C# 6.0 and 7.0 is the expression bodied members. It simply allows you to write single-line expressions for your properties and methods - in this case, let's look at how to use it for your get/set methods in a way that takes up less space and requires slightly less typing:

private string name;
public string Name
{
	get => name;
	set => name = value;
}

If your property is read-only, the syntax can be even shorter:

public string Name => "John Doe";

Of course this also works if you need to actually do something before returning the value, like this:

public string Name { get; set; } = "John Doe";

public string FirstName => this.Name.Substring(0, this.Name.IndexOf(" "));

As you can see, this allows you to define a get method but without the actual get and return keywords, while encouraging you to keep it all in one line instead of multiple lines.

Summary

Properties gives your classes more control over how fields can be accessed and manipulated, and they should always be used when you want to give access to fields from outside of the declaring class.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!