属性允许源代码用一个简化的语法来调用一个方法。
CLR支持两种属性:无参属性 (parameterless property) ,有参属性(parameterful property)
C#中将有参属性称为索引器(indexer)
10.1无参属性
一般用类型的字段成员来实现获取或改变类型的状态信息。
面向对象设计和编程的重要原则之一就是数据封装(data encapsulation),它意味着类型的字段永远不应该公开。强烈建议将所有的字段都设为private。
要允许获取类型状态信息,就公开一个针对该用途的方法。
封装了字段访问的方法通常称为访问器(accessor)方法(如下面的GetName,SetName)。访问器方法可以对数据的合理性进行检查,确保对象的状态不被破坏。
public sealed class Employee
{
private String m_Nmae;
private Int32 m_Age;
public String GetName()
{
return m_Nmae;
}
public void SetName(String value)
{
m_Nmae = value;
}
public Int32 GetAge()
{
return m_Age;
}
public void SetAge(Int32 value)
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("value", value.ToString(), "The value must be grater than or equal to 0");
}
m_Age = value;
}
public static void Main()
{
Employee e = new Employee();
e.SetName("Jeffery Richter");
String EmployeeName = e.GetName();
e.SetAge(41);
e.SetAge(-5);
Int32 EmployeeAge = e.GetAge();
}
}
将SetXxx方法标记为protected,就可以实现只允许派生类型修改值。
以上代码中,类型的用户必须调用方法,而不能直接引用一个字段名。
编程语言和CLR还提供了一种称为属性(property)的机制,如下:
public sealed class Employee
{
private String m_Nmae;
private Int32 m_Age;
public String Name
{
get { return (m_Nmae); }
set { m_Nmae = value; }
}
public Int32 Age
{
get { return (m_Age); }
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("value", value.ToString(), "The value must be greater than or equal to 0");
}
m_Age = value;
}
}
public static void Main()
{
Employee e = new Employee();
e.Name = "Jeffery Richter";
String EmployeeName = e.Name;
e.Age = 41;
e.Age = -5;
Int32 EmployeeAge = e.Age;
}
}
可将属性想象成智能字段(smart field),即背后有额外逻辑的字段。
每个属性都有一个名称(Name,Age)和一个类型(String,Int32不能为void)。属性不能重载。定义属性时,可以省略set方法来定义一个只读属性,或者省略get方法来定义一个只写属性。
通过属性的get和set方法来操作类型内私有的字段,是一种很常见的做法。
以前面的Employee类型为例。编译器编译这个类型时,会发现其中的Name和Age属性。由于两个属性都有get和set访问器方法,所以编译器在Employee类型中生成4个方法定义。
10.1.1自动实现的属性
如果只是为了封装一个支持字段而创建一个属性,C#还提供了更简单的语法,称为自动实现的属性(automatically Implemented Property)。
10.1.2合理定义属性
我个人不喜欢属性
10.1.3对象和集合初始化器
System.Collections命名空间包含可使用的集合类和相关的接口,提供了集合的基本功能。
IEnumerable 接口
System.Collections
该枚举数支持在非泛型集合上进行简单迭代
所有继承了IEnumerable的类,要使用foreach迭代器时,就需要使用该方法。因此也只有实现了该接口的类才可以使用foreach。
名称 |
说明 |
GetEnumerator() |
返回循环访问集合的枚举数。 |
IList 接口
System.Collections
IList 是 ICollection 接口的子代,并且是所有(非???)泛型列表的基接口
IList继承自ICollection
名称 |
说明 |
Add(Object) |
将某项添加到 IList 中。 |
Clear() |
从 IList 中移除所有项。 |
Contains(Object) |
确定 IList 是否包含特定值。 |
CopyTo(Array, Int32) |
从特定的 Array 索引处开始,将 ICollection 的元素复制到一个 Array 中。(从 ICollection 继承。) |
GetEnumerator() |
返回循环访问集合的枚举数。(从 IEnumerable 继承。) |
IndexOf(Object) |
确定 IList 中特定项的索引。 |
Insert(Int32, Object) |
将一个项插入指定索引处的 IList。 |
Remove(Object) |
从 IList 中移除特定对象的第一个匹配项。 |
RemoveAt(Int32) |
移除指定索引处的 IList 项。 |
ICollection<T> 接口
System.Collections.Generic
定义操作泛型集合的方法。
ICollection继承自IEnumerable
名称 |
说明 |
Add(T) |
将某项添加到 ICollection<T> 中。 |
Clear() |
从 ICollection<T> 中移除所有项。 |
Contains(T) |
确定 ICollection<T> 是否包含特定值。 |
CopyTo(T[], Int32) |
从特定的 Array 索引开始,将 ICollection<T> 的元素复制到一个 Array 中。 |
GetEnumerator() |
返回一个循环访问集合的枚举器。(从 IEnumerable<T> 继承。) |
Remove(T) |
从 ICollection<T> 中移除特定对象的第一个匹配项。 |
ICollection主要针对静态集合;IList主要针对动态集合。
如果一个方法的返回值是IEnumerable<T> ,必须在方法后面使用.ToList()方法才能得到一个集合数据。
集合的初始化被认为是相加(Additive)操作,而非替换的操作。编译器发现Student属性的类型是List<String>,而且这个类型实现了IEnumerable<String>接口。如下:
public sealed class Classroom
{
private List<String> m_students = new List<String>();
public List<String> Students { get { return m_students; } }
public Classroom() { }
public static void Main()
{
Classroom classroom = new Classroom
{
Students = { "Chris","Jeff" }
};
//Classroom classroom = new Classroom();
//classroom.Students.Add("Chris");
//classroom.Students.Add("Jeff");
foreach (var student in classroom.Students)
Console.WriteLine(student);
}
}
10.1.4 匿名类型
10.1.5 System.Tuple类型
10.2 有参属性
重看