5.C#编程指南-属性
属性是这样的成员:它提供灵活的机制来读取、编写或计算某个私有字段的值。可以像使用公共数据成员一样使用属性,但实际上它们是称作“访问器”的特殊方法。这使得可以轻松访问数据,此外还有助于提高方法的安全性和灵活性。
在本示例中,TimePeriod类存储一个时间段。在内部,类以秒为单位存储时间,但客户端使用名为Hours的属性能够以小时为单位指定时间。Hours属性的访问器执行小时和秒之间的转换。
class TimePeriod { private double seconds; public double Hours { get { return seconds / 3600; } set { seconds = value * 3600; } } }
属性概述
1.属性使类能够以一种公开的方法获取和设置值,同时隐藏实现或验证代码。
2.get属性访问器用于返回属性值,而set访问器用于分配新值。这些访问器可以有不同的访问级别。
3.value关键字用于定义由set取值函数分配的值。
4.不实现set取值函数的属性是只读的。
5.对于不需要任何自定义访问器代码的简单属性,可以考虑使用自动实现的属性。
使用属性
属性结合了字段和方法的多个方面。对于对象的用户,属性显示为字段,访问该属性需要相同的语法。对于类的实现者,属性是一个或两个代码块,表示一个get访问器或一个set访问器。当读取属性时,执行get访问器的代码块;当向属性分配一个新值时,执行set访问器的代码块。不具有set访问器的属性被视为只读属性。不具有get访问器的属性被视为只写属性。同时具有这两个访问器的属性是读写属性。
与字段不同,属性不作为变量为分类。因此,不能将属性作为ref参数或out参数传递。
属性具有多种用法:
1.它们可在允许更改前验证数据。
2.它们可透明地公开某个类上的数据,该类的数据实际上是从其他源(例如数据库)检索到的。
3.当数据被更改时,它们可采取行动,例如引发事件或更改其他字段的值。
属性在类块中是按以下方式来声明的:指定字段的访问级别,接下来指定属性的类型和名称,然后跟上声明get访问器或set访问器的代码块。
接口属性
可以在接口上声明属性。以下是接口索引器访问器的示例:
public interface ISampleInterface { // Property declaration: string Name { get; set; } }
如果Employee类实现两个接口ICitizen和IEmployee,并且两个接口都具有Name属性,则需要显示接口实现。
public class Employee:IEmployee,ICitizen { string IEmployee.Name { get { return "Employee Name"; } set { } } string ICitizen.Name { get { return "Citizen Name"; } set { } } }
限制访问器可访问性
属性或索引器的get和set部分称为“访问器”。默认情况下,这些访部器具有相同的可见性或访问级别。
private string name = "Hello"; public string Name { get { return name; } protected set { name = value; } }
对属性或索引器使用访问修饰符受以下条件的制约:
1.不能对接口或显示接口成员实现使用访问器修饰符。
2.仅当属性或索引器同时具有set和get访问器时,才能使用访问器修饰符。这种情况下,只允许对其中一个访问器使用修饰符。
3.如果属性或索引器具有override修饰符,则访问器修饰符必须与重写的访问器的访问器(如果有的话)匹配。
4.访问器的可访问级别必须比属性或索引器本身的可访问性级别具有更严格的限制。
重写访问器的访问修饰符:
在重写属性或索引器时,被重写的访问器对重写代码而言,必须是可访问的。此外,属性/索引器和访问器的可访问级别都必须与相应的被重写属性/索引器和访问器匹配。
public class Parent { public virtual int TestProperty { // Notice the accessor accessibility level. protected set { } // No access modifier is used here. get { return 0; } } } public class Kid : Parent { public override int TestProperty { // Use the same accessibility level as in the overridden accessor. protected set { } // Cannot use access modifier here. get { return 0; } } }
实现接口:
使用访问器实现接口时,访问器不能具有访问修饰符。但是,如果使用一个访问器(如get)实现接口,则另一个访问器可以具有访问修饰符,示例如下:
public interface ISomeInterface { int TestProperty { // No access modifier allowed here // because this is an interface. get; } } public class TestClass : ISomeInterface { public int TestProperty { // Cannot use access modifier here because // this is an interface implementation. get { return 10; } // Interface property does not have set accessor, // so access modifier is allowed. protected set { } } }
访问器可访问性域:
如果对访问器使用访问某个修饰符,则访问器的可访问性域由该修饰符确定。
如果不对访问器使用访问修饰符,则访问器的可访问性域由属性或索引器的可访问性级别确定。
public class BaseClass { private string name = "Name-BaseClass"; private string id = "ID-BaseClass"; public string Name { get { return name; } set { } } public string Id { get { return id; } set { } } } public class DerivedClass : BaseClass { private string name = "Name-DerivedClass"; private string id = "ID-DerivedClass"; new public string Name { get { return name; } // Using "protected" would make the set accessor not accessible. set { name = value; } } // Using private on the following property hides it in the Main Class. // Any assignment to the property will use Id in BaseClass. new private string Id { get { return id; } set { id = value; } } } class MainClass { static void Main() { BaseClass b1 = new BaseClass(); DerivedClass d1 = new DerivedClass(); b1.Name = "Mary"; d1.Name = "John"; b1.Id = "Mary123"; d1.Id = "John123"; // The BaseClass.Id property is called. System.Console.WriteLine("Base: {0}, {1}", b1.Name, b1.Id); System.Console.WriteLine("Derived: {0}, {1}", d1.Name, d1.Id); // Keep the console window open in debug mode. System.Console.WriteLine("Press any key to exit."); System.Console.ReadKey(); } } /* Output: Base: Name-BaseClass, ID-BaseClass Derived: John, ID-BaseClass */ //注意,如果将 new private string Id 替换为 new public string Id,则得到如下输出: //Name and ID in the base class: Name-BaseClass, ID-BaseClass //Name and ID in the derived class: John, John123
自动实现的属性
在C#3.0和更高版本中,当属性的访问器中不需要其他逻辑时,自动实现的属性可使属性声明更加简洁。客户端代码还可通过这些属性创建对象。
public double TotalPurchases { get; set; } public string Name { get; set; } public int CustomerID { get; set; }
如何:使用自动实现的属性实现轻量类
对于自动实现的属性,需要get和set访问器。要使该类不可变,请将set访问器声明为private。但是,声明私有set访问器时,不能使用对象初始化来初始化属性。
必须使用构造函数或工厂方法。编译器为每个自动实现的属性创建了后备字段。这些字段无法直接从源代码进行访问。
// This class is immutable. After an object is created, // it cannot be modified from outside the class. It uses a // constructor to initialize its properties. class Contact { // Read-only properties. public string Name { get; private set; } 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 cannot be modified from outside the class. It uses a // static method and private constructor to initialize its properties. public class Contact2 { // Read-only properties. public string Name { get; private set; } public string Address { get; private set; } // 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 = {"Terry Adams","Fadi Fakhouri", "Hanying Feng", "Cesar Garcia", "Debra Garcia"}; 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 cannot 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 cannot be modified by client code. // CS0272: // list2[0].Name = "Eugene Zabokritski"; // Keep the console open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); } } /* Output: Terry Adams, 123 Main St. Fadi Fakhouri, 345 Cypress Ave. Hanying Feng, 678 1st Ave Cesar Garcia, 12 108th St. Debra Garcia, 89 E. 42nd St. */