C#无参属性
永远不要公开类型的字段,面向对象设计和编程的重要原则之一就是 数据封装。如果公开类型的字段,会很容易的写出不恰当使用字段的代码。这里可以把字段声明为private,然后声明访问器方法。
class test { private string Name; public void SetName(string value) { Name = value; } public string GetName() { return Name; } }
复制代码
实现只读或只写就是这么简单,不实现一个索引器方法就行了。还可以将Setname方法标记为protected,就可以只允许派生类修改了。聪明的你也发现上面这个做法的缺点了吧,首先得去实现额外的方法,所以会产生更多的代码,其次,如果调用的时候必须调用方法,不能直接引用一个字段名。还好C#给我们提供了一个称为属性(property)的机制,它缓解了第一个缺点造成的影响,也消除了第二个缺点。
class test { private string name; public string Name { get { return name; } set { if ("狗剩" == value) { Console.WriteLine("系统崩溃,抛出异常"); return; } name = value; } } }
复制代码
属性可以用任意的可访问修饰符来标记。不能定义名称相同的属性。
如果只是为了封装一个支持字段而创建属性,C#还提供一一种更简单的语法,称为自动实现的属性(Automatically Implemented Property,简称AIP)。like this
class test { public int Age { get; set; } }
复制代码
如果声明一个属性而不提供get/set实现,C#会自动为你声明一个私有字段。原始代码是这样的:
public int Age{ [CompilerGenerated] get { int num; num = this.<Age>k__BackingField; Label_0009: return num; } [CompilerGenerated] set { this.<Age>k__BackingField = value; return; }}
复制代码
和直接声明一个public 的Age字段相比,自动实现的属性的优势在哪?两者存在一处重要的区别:使用自动实现的属性(AIP)意味着已经创建一个属性,访问该属性的时候会调用get/set方法。如果有一天你决定自己实现这个AIP的get/set,而不接受编译器默认的实现,那么访问这个属性的任何代码都不必重新编译。如果是一个public的Age字段,如果你把它更改为属性,那么访问这个字段的所有代码就都需要重新编译了。
凡事都有好有坏,那么AIP有哪些不讨人喜欢的地方呢:
1.如果使用字段声明的语法,可以包含初始化的部分,如果使用AIP就不行了,必须在咩个构造器中显示的初始化每个AIP。
2.AIP支持的字段名是由编译器决定的,而且每次重新编译,这个字段名可能会发生更改,这样一来,只要含有一个AIP就不能对类型的实例进行反序列化了。在任何想要序列化或反序列化的类型中,都不要使用AIP功能。
3.不能在AIP的get/set添加断点,所以不好检测程序在什么时候获取或设置这个属性。咱手动实现的就可以设置断点,不信你试试。
AIP功能是作用于整个属性的:要么都用,要么都不用,这意味这,如果显式的实现get那么set也要显式实现,反之亦然。
属性看起来与字段相似,但本质上是方法。属性与字段的区别如下:
1.属性可以是只读或只写的(get/set访问器方法),字段却总是可读可写的。
2.属性方法可能抛出异常,字段访问不会抛出。
3.属性不能作为out或ref参数传递给方法,字段可以。
class Program { public int Age { get {return 3 ;} set{} } int age; static void Main() { var t = new { name="XiaoBai",age=22 }; test tt = new test(); //tt.Name = "狗剩"; Program p = new Program(); some(out p.age); some(out p.Age);//属性,索引器或动态成员不得作为out或ref参数传递 Console.ReadKey(); } static void some(out int age) { age = 10; } }
复制代码
4.属性方法可能花费较长时间执行,字段的访问都是立即完成。
5.属性方法可能需要额外的内存,或者返回一个不正确的引用,指向不属于对象状态一部分的某个东西,这样一来,对返回对象的修改就作用不到原始对象身上了,相反查询字段返回的总是正确的引用,它指向的东西保证是原始对象状态的一部分。使用会返回一个拷贝的属性时,非常容易引起混淆。
如果仔细研究下属性和字段的差别,你会发现只有在极个别的情况下属性才真正有用。属性唯一的好处就是提供了简化的语法,和调用普通方法(非属性中的方法)相比,属性不仅不会提升代码的性能,还会妨碍对代码的理解。
属性访问器的可访问性:
我们有时希望为get访问器指定一种可访问性,为set访问器指定另一种访问器:
class test { public int Age { get { return age; } protected set { if (value < 0) Console.WriteLine("年龄错误!"); else { age = value; } } } int age; }
复制代码
如上所述,Age属性本身声明为public,意味这get访问器方法是公共的,可由所有代码访问,但是,注意set访问器方法被声明为protected,只能从test的内部定义的代码中调用,或者从test的派生类的代码中调用。
定义一个属性时,如果两个访问其方法需要具有不同的可访问性,C#语法要求必须为属性本身指定限制最不大的那一种可访问性,然后在两个访问其中,只能选择一个来应用限制较大的那一种可访问性。在前面的例子中,属性本身为public,set访问器为protected(限制比public大)。
class test { private int Age { get { return age; } protected set { if (value < 0) Console.WriteLine("年龄错误!"); else { age = value; } }//这里就通不过编译 } int age; }