属性允许源代码用一个简单的语法来调用一个方法。CLR支持两种属性:无参属性 和 有参属性(C#里称为 索引器)
无参属性
数据封装是面向对象设计和编程中的一个重要的原则,它意味着类型的字段永远不应该公开,应为这样很容易写出不恰当的使用字段的代码,从而破坏对象的状态。 如:
public class Employee{
public String Name;
public Int32 Age;
}
Employee e=new Employee(); e.Age=-5;
可以使用属性
public class Employee{
private String m_Name;
private Int32 m_Age;
public Int32 Age{
get{return m_Age;}
set{ if(value>0){m_Age=value;} }
}
}
每个属性都有一个名称和一个类型(不能是void) 属性不能重载,就是说,不能定义名称相同、类型不同的两个属性。
get和set方法不一定要访问支持字段。
其实编译器在编译时会将每个访问器方法写成一个单独的方法,在前面加get_或set_ 本处就是get_Age() 和 set_Age(Int32 value)
C#还支持一种简单的语法称为自动实现的属性(AIP) 如下:
public class Employee{
public String Name{get;set;}
}
由于AIP没有提供get和set的实现,C#会自动为你声明一个私有字段,该字段名称是由编译器决定的,而且每次重新编译代码,它都可能更改这个名字。这样一来,任何类型只要含有一个AIP,便没办法对该类型的实现进行反序列化了。
关于对象和集合初始化器 匿名类型 看这里
需要补充的是
集合初始化器语法的使用,必须是该集合类提供了Add方法才行。
编译器在构建匿名类型时还会重写Object的Equals、GetHashCode、ToString方法。前两个方法使匿名类型的实例可以放到一个哈希表集合中。
上面链接里讲的匿名类型有一些错误:编译器创建匿名类型时,生成的无参属性是只读的(没有set访问器),匿名类型的字段的初始化是通过构造函数来初始化的。所以匿名类型一经创建(通过构造函数初始化字段)就不可变了(所有属性都是只读的)
有参属性(索引器)
public class Employee{
private String m_Name;
public String this[int i]{
get{ if(i>0){return m_Name;} }
set{ if(i<0){m_Name=value;}}
}
}
Employee e=new Employee(); string s= e[1]; e[2]="value";
编译器编译索引器时原始代码可能如下:
public class Employee{
private String m_Name;
public String get_Item(int i){...}
public String set_Item(int i,string value){...}
}
所以索引器的get访问器接受一个或多个参数(本处至少有一个int i) set访问器接受两个或多个参数(本处至少有一个int i和一个value)
索引器至少要有一个参数(本处是int i),但可以更多。 参数和返回类型可以是除了void以外的任何类型。
C#编译器默认的索引器的访问器名称是get_Item和set_Item,因为声明索引器时不像无参属性那样可以由开发人员命名,只能是this[...]。 但可以通过向索引器应用System.Runtime.CompilerServices.IndexerNameAttribute定制attribute来重命名这些索引器的访问器名称:
public class Employee{
[IndexerName("Str")]
public String this[int i]{...}
}
这样编译的访问器名称将是get_Str和set_Str
在C#中,一个类型可定义多个索引器,只要索引器的参数集不同,如下由于参数集相同,所以不能通过编译。
public class Employee{
public int32 this[Boolean b]{...}
public String this[Boolean b]{...}
}
可以为get和set设置不同的访问性
public class Employee{
private String m_Name;
public String Name{
get{...}
protected set{...}
}
}