字段和属性(C#)

类成员默认为私有成员。如果不为类成员添加访问修饰符,那么默认的是private。

公共成员需要显式指定。


字段和属性是不同的两个概念:

字段是类中实打实的一种变量。用来存储与对象相关联的数据。所以当字段规定为public时,在当前类之外其他类中

时随地访问和修改字段值(只要定义好当前类的实例即可,除static外),也就是类中所有的细节都能看的很清

楚。这符合封装的思想,但却不是彻底的封装,因为它并不安全。

所以我们又可以规定字段的访问修饰符为private,这样只有在当前类中可以访问,在类之外的其他类就访问和修改不

到了。


但是以上又引申出两个方面的问题:

1.private封装的太过彻底,public开放的太大,而当我们需要规定字段只读或者只写怎么办?

2.我们可以在类之外看到类中有哪些字段,知道类中具体的字段名是什么,这不符合安全原则。


为了解决以上问题,当然可以将所有字段设置为private,再专门为每个字段提供公共取值和赋值方法用于访问和修改

它们的值。但是这样的代价是我们无法再使用赋值操作符设置类中的字段数据(即我们不能用类的实例.字段名 =

值来设定),必须调用对应的方法来访问修改字段数据。


因此才最终推导出属性这个东西。

C#本质论的书上说,属性的关键特点是,提供了从编程角度看类似于字段的API(个人认为这句话最为贴切属性的作用

和含义)。

不懂属性到底是什么或者只知道用却不去深究其与字段关系的,看到这句话多少会有些豁然开朗的感觉。


属性有哪些好处呢:

1.解决直接能看出类中字段名的弊端。类的字段名如果是_PhoneNumber,那么对应的属性名可以定义为PhoneNumber。

我们在类外的其他类中,可以通过类的实例.PhoneNubmer来进行赋值和访问字段数据,但是其实真正的字段名是_PhoneNumber,但事实上调用的人并不知道。

2.我们又可以用赋值操作符进行访问和修改类的成员数据。

3.定义只读只写属性更为方便。

4.利用属性可以提供属性验证,在将value赋给字段之前,可以拦截赋值,先对其进行验证。


在实际编程过程中,要一直使用属性,不要直接调用字段。要不然属性还有毛线意义。

另外属性不能作为ref和out参数。

对于只读属性,利用类构造器依旧不可以对其进行赋值,只能通过字段初始化赋值。

属性可以作为虚字段使用。即可以让属性取值方法返回计算好的值,让赋值方法解析值,解析的值存到其他成员字段

中,而属性本身并没有实质的意义。


请看下面一个例子:

public PropertyExample()
{
    this.InitializeComponent();

    Phone phone = new Phone();

    phone.Brand = "Microsoft";
    txtblk1.Text = string.Format("手机品牌是:{0}",phone.Brand);
    phone.PhoneNubmer = "123456789";
    txtblk2.Text = string.Format("手机号码是:{0}", phone.PhoneNubmer);

    //错误,无法为属性PhoneType赋值,因为它是只读的
    //对于只读属性,我们只能利用字段对其进行赋初始值
    //phone.PhoneType = "Lumia 950XL";
    txtblk3.Text = string.Format("手机型号是:{0}", phone.PhoneType);

    phone.PhoneSDSize = "64GB";
    //错误,因为属性PhoneSDSize是只写的,不能读取值;所以就功能上来说只写的情况是很罕见的
    //只写的属性一般你进行了赋值却不能最终输出获取到属性值,所以比较少见
    //txtblk3.Text = string.Format("手机SD卡容量:{0}", phone.PhoneSDSize);

    //虽然给属性PhonePrice设置了get,set,但是set用private修饰符修饰,那么在类的外部set访问器便不可访问
    //phone.PhonePrice = "3599";
    phone.MakePrice("3599");
    txtblk4.Text = string.Format("手机价格是:{0}",phone.PhonePrice);

    txtblk5.Text = string.Format("手机屏幕尺寸:{0}", phone.PhoneScreenSize);
    phone.PhoneScreenSize = "5.7";
    txtblk6.Text = string.Format("修改后的手机屏幕尺寸:{0}", phone.PhoneScreenSize);
}

//以下属性只用于说明问题,与属性字面意义并无挂钩,无需纠结
class Phone
{

    //属性第一种定义方法
    private string _Brand;
    public string Brand
    {
        get
        {
            return _Brand;
        }
        set
        {
            _Brand = value;
        }
    }

    //属性第二种定义方法
    public string PhoneNubmer { get; set; }

    //只读属性(只能通过赋初始值的方式赋值,并不能通过类构造器赋值)
    private string _PhoneType = "Lumia 1320";
    public string PhoneType
    {
        get
        {
            return _PhoneType;
        }
    }

    //只写属性
    private string _PhoneSDSize;
    public string PhoneSDSize
    {
        set
        {
            _PhoneSDSize = value;
        }
    }

    //为属性的取值方法和赋值方法指定访问修饰符
    //赋值方法指定private修饰符后,PhonePrice属性在Phone类内部是可读可写的,在Phone类之外只读
    private string _PhonePrice;
    public string PhonePrice
    {
        get
        {
            return _PhonePrice;
        }
        private set
        {
            _PhonePrice = value;
        }
    }

    //因为类的所有成员包括方法,都是默认私有的
    //所以在要决定价格的函数必须明确指定为public才能在类的外部被调用
    //也可以利用类的构造器对属性进行赋值,因为在类内部属性PhonePrice可读可写(跟只读属性比较)
    public void MakePrice(string price)
    {
        //注意这边也可以写_PhonePrice = price;
        //但是出于需要优先使用属性的原则,不要直接调用字段进行赋值
        PhonePrice = price;
    }

    //对字段进行初始化赋值
    private string _PhoneScreenSize = "5.5";
    public string PhoneScreenSize
    {
        get
        {
            return _PhoneScreenSize;
        }
        set
        {
            _PhoneScreenSize = value;
        }
    }

}

结果截图:


猜你喜欢

转载自blog.csdn.net/u010792238/article/details/50808671