C#中的属性

来源:https://blog.guoqianfan.com/2019/12/07/properties-in-csharp/

前言

C#属性是字段的扩展,它配合C#中的字段使用,用以构造一个安全的应用程序。

属性提供了灵活的机制来读取、编写或计算私有字段的值,可以像使用公共数据成员一样使用属性,但实际上它们是称做“访问器”的特殊方法,其设计目的主要是为了实现面向对象(Object Oriented, OO)中的封装思想。

根据该思想,字段最好设为private, 一个设计完善的类最好不要直接把字段声明为公有或受保护的,以阻止客户端直接进行访问,其中一个主要原因是,客户端直接对公有字段进行读写,使得我们无法对字段的访问进行灵活的控制,比如控制字段只读或者只写将很难实现。

—— 姜晓东《C# 4.0权威指南》-【9.4.5 属性

声明和使用读/写属性(旧)

这种方式是C#中最基础的,也是最早出现的读写属性的方式。本文中我暂时称它为老式的读/写属性

该方式允许我们在对属性读/写时,进行一些操作/计算

声明属性

首先要声明一个私有字段,然后使用get访问器读取私有字段的值,使用set访问器为私有字段赋值

示例代码如下:

class Person
{
    private string _name = "N/A";
    private int _age = 0;

    // Declare a Name property of type string:
    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
        }
    }

    public int Age
    {
        get
        {
            return _age;
        }
        set
        {
            _age = value > 120 ? 120 : value;
        }
    }
}

使用属性

属性的使用很简单。外部可以直接对属性进行读取或赋值,就像使用类的公共字段一样。(注意:这里假设属性都是公共读写的,实际使用中要注意属性的可访问性

//==========外部访问属性==========
Person person = new Person();
person.Name = "Bob";//为属性赋值
person.Age = 18;//为属性赋值

//读取属性的值
int age = person.Age;

备注

  1. 在属性的set方法中,value变量是很特殊的, 它代表用户指定的值。

自动实现的属性(新)

当属性访问器中不需要任何其他逻辑时,我们可以使用自动实现的属性,它会使属性声明更加简洁。

自动实现的属性在编译后,也是生成了老式的读/写属性

VS中使用快捷键prop可以快速生成自动实现属性。

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

其他

自动实现的属性的本质

自动实现的属性在编译后,也是生成了老式的读/写属性

这个是编译器自动帮我们做的,可以通过查看编译后生成的IL代码(又称作MSILCIL)来验证。

不过本人能力有限,就不分析了。这里推荐大家阅读 《C# 4.0权威指南》 中的【9.4.5 属性】一节,该章节详细分析了自动实现的属性经过编译后生成的IL代码。

属性初始化器

在 C# 6 和更高版本中,你可以像字段一样初始化自动实现属性:

public string FirstName { get; set; } = "Jane";  

上述代码经过编译后,是在构造函数中,为属性赋值的。(来源)

只读属性默认初始化

在 C# 6 中,可以去掉set访问器,使属性变为只读属性。

public string Name { get; } = "hello world";

上述代码经过编译后,生成的属性关联字段是readonly的,并且仍然是在构造函数中为属性赋值的:private readonly string kBackingField;。(来源)

这种方式下,生成的属性是没有 setter 的(即使用反射,也无法设置值,setter 根本就不存在)。这个属性只能在构造函数中,或者结合特性赋值。(来源)

表达式体属性

自 C# 6 起,支持方法、运算符和只读属性的表达式主体定义。

自 C# 7.0 起,支持构造函数、终结器、属性和索引器访问器的表达式主体定义。

在 C# 6 中,可以把只读属性改写为表达式体的形式。

在 C# 7.0 中,可以把某个访问器改写为表达式体的形式。

只读属性的表达式体形式属性(访问器)的表达式体形式是不冲突的,因为它们的使用场景不一样(写法也不一样)。

因为都是表达式体形式,它们具有相同的限制:要求方法体能够改写为lambda表达式(必须是单行代码)。

只读属性的表达式体形式(C#6)

只读属性的表达式体形式有2个限制

  • 只包含 get访问器
  • 要求 get访问器的方法体能够改写为lambda表达式(必须是单行代码

示例代码如下:

//C# 5
public string FullName
{
    get
    {
        return FirstName + "" + LastName;
    }
}

//C# 6
public string FullName => FirstName + "" + LastName;

我们可以通过VS的智能提示看到:该属性只有get访问器。

属性访问器的表达式体形式(C#7)

在 C# 7.0 中,对于老式的读/写属性,我们可以把get访问器或set访问器改写为表达式体(lambda)。

注意:要求访问器的方法体能够改写为lambda表达式(必须是单行代码

示例代码如下:

//C# 5
private int _id;
public int Id
{
    get
    {
        return _id;
    }
    set
    {
        _id = value;
    }
}

//C# 7.0
private int _id;
//全部改写为表达式体
public int Id { get => _id; set => _id = value; }
//只改写set访问器
public int Id { get { return _id; } set => _id = value; }
//只改写get访问器
public int Id { get => _id; set { _id = value; } }

备注

下面的备注,对于老式的读/写属性自动实现的属性都是通用的。

  1. 可以给访问器设置可访问性。例如把set访问器设为private,不允许外部直接赋值。通常是限制set访问器的可访问性。更多请参考:限制访问器可访问性(C# 编程指南)
  2. 可以省略掉某个访问器。通常是省略掉set访问器可使属性为只读。

另外,属性的本质是方法,所以接口中可以包含属性

参考

  1. 如何:声明和使用读/写属性(C# 编程指南):https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/classes-and-structs/how-to-declare-and-use-read-write-properties
  2. 自动实现的属性(C# 编程指南):https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/classes-and-structs/auto-implemented-properties
  3. 限制访问器可访问性(C# 编程指南):https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/classes-and-structs/restricting-accessor-accessibility
  4. => 运算符(C# 参考):https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/operators/lambda-operator
  5. 探索C#之6.0语法糖剖析:https://www.cnblogs.com/mushroom/p/4666113.html
  6. 姜晓东《C# 4.0权威指南》-【9.4.5 属性
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容