属性 Property
作者:张齐天
链接:https://www.zhihu.com/question/410257728/answer/1375205727
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Part 1 封装啊,少年!
属性最早是没有简化写法的。最开始属性是写作
private int _a; // field.
public int A // property corresponding to the field '_a'.
{
get { return _a; }
set { _a = value; }
}
的。首先,这个写法有一个好处,它可以避免和防止对 _a 乱赋值,因为我们可以为 set 块里添加数据处理逻辑,比如数据验证逻辑。假设这个 A 表示一个人的年龄,那么我们至少可以知道,年龄是不可能低于 0 的(至于多大,暂时设置个 150 吧)。那么处理逻辑可以改写成这样:
private int _a;
public int A
{
get { return _a; }
set
{
if (value < 0 || value > 150)
throw new ArgumentException("The value is out of range.", nameof(value));
_a = value;
}
}
于是,这样就避免了其他程序员使用字段的时候,为其随意赋值的情况。大多数情况,这样比起直接一个 public 的字段要好。OOP 的本质就是封装。
如果你不把字段封装起来,你自己知道怎么用,那你平时肯定没啥问题,只是别人看到你的代码,可以随便找到“漏洞”。
Part 2 自动属性?
然后来说一下这个:自动属性。
自动属性是 C# 3 才有的一种规定写法。要是一个单纯的 get
和 set
块,那么可以简写:public int A { get; set; }
那么问题来了。这玩意儿都没有任何数据校验的操作了,请问这个跟一个 public
字段又有什么区别呢?你忽略了一点。自动属性是可以在 set
和 get
块的前面放一个访问修饰符的。
比如,我们可以让 A 属性只能在当前类里面才可以随意赋值,此时我们可以为 set
块添加 private
修饰:public int A { get; private set; }
这样,数据 A 就不能在外面随便赋值了。另外,这么写不等于 private _a
字段声明。因为 A 的值是可以在类外面的任何位置用到,只是无法修改,所以单纯的 private _a
和 public A { get; private set; }
并不等价。
Part 3 属性还有别的好处吗?
前面已经说到了一些好处,这里再说一个。属性是一个包装器,它包含两个方法,get 和 set。这两个方法类比 Java 其实就知道它只是 getxxx() 和 setxxx(int value) 的简化写法而已。
既然是两个方法,那么执行操作就是不占内存空间的。说白了,在不多出任何内存的时候就可以为类的字段添加处理逻辑,这是不是一个很有趣的行为呢?
实际上,我们的这些代码是有一个专门存放代码的内存区域,叫做代码区。但它们和我们知道的栈内存和堆内存是完全分开的。我说它不占内存是因为,它并不影响这个类型的存储空间。
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Explicit)]
public struct C
{
[field: FieldOffset(0)]
public int A { get; set; }
}
public static class Program
{
private static void Main() => Console.WriteLine(Marshal.SizeOf<C>());
}
和
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Explicit)]
public struct C
{
[FieldOffset(0)]
private int _a;
}
public static class Program
{
private static void Main() => Console.WriteLine(Marshal.SizeOf<C>());
}
都是占据 4 个字节