美文网首页
C# 创建类型 03

C# 创建类型 03

作者: JeetChan | 来源:发表于2019-08-08 21:00 被阅读0次

    声明

    本文内容来自微软 MVP solenovex 的视频教程——真会C#?- 第3章 创建类型,大致和第 3 课—— class - 对象初始化、this、属性、索引器 对应。可在 GitHub 中查看 C# 视频教程的配套PPT
    下图为配套的小程序习题:

    C# 小程序习题

    本文主要包括以下内容:

    1. 对象初始化
    2. this 引用
    3. 属性(Properties)
    4. 索引器

    对象初始化器

    对象任何可访问的字段/属性在构建之后,可通过对象初始化器直接为其进行设定值。

    public class Bunny
    {
        public string Name;
        public bool LikesCarrots;
        public bool LikesHumans;
        public Bunny () {}
        public Bunny (string n) { Name = n; }
    }
    
    // Note parameterless constructors can omit empty parentheses
    Bunny b1 = new Bunny { Name="Bo", LikesCarrots=true, LikesHumans=false };
    Bunny b2 = new Bunny ("Bo") { LikesCarrots=true, LikesHumans=false };
    

    编译器生成的代码

    临时变量可以保证:如果在初始化的过程中出现了异常,那么不会以一个初始化到一半的对象来结尾。

    Bunny temp1 = new Bunny(); // temp1 is a compiler-generated name
    temp1.Name = "Bo";
    temp1.LikesCarrots = true;
    temp1.LikesHumans = false;
    Bunny b1 = temp1;
    Bunny temp2 = new Bunny ("Bo");
    temp2.LikesCarrots = true;
    temp2.LikesHumans = false;
    Bunny b2 = temp2;
    

    对象初始化器 vs 可选参数

    如果不使用初始化器,上例中的构造函数也可以使用可选参数。

    可选参数方式
    优点:可以让Bunny类的字段/属性只读
    缺点:每个可选参数的值都被嵌入到了calling site,C#会把构造函数的调用翻译成:Bunny b1 = new Bunny ("Bo", true, false);

    public Bunny (string name,
    bool likesCarrots = false,
    bool likesHumans = false)
    {
        Name = name;
        LikesCarrots = likesCarrots;
        LikesHumans = likesHumans;
    }
    
    // This would allow us to construct a Bunny as follows:
    Bunny b1 = new Bunny (name: "Bo",
    likesCarrots: true);
    

    this 引用

    this 引用指的是实例的本身。

    public class Panda
    {
        public Panda Mate;
        public void Marry (Panda partner)
        {
            Mate = partner;
            partner.Mate = this;
        }
    }
    

    this 引用可以让你把字段与本地变量或参数区分开。只有 class/struct 的非静态成员才可以使用 this。

    public class Test
    {
        string name;
        public Test (string name) { this.name = name; }
    }
    

    属性(Properties)

    外边来看,属性和字段很像。但从内部看,属性含有逻辑,就像方法一样。属性的声明和字段的声明很像,但多了一个 get ,set 块。

    Stock msft = new Stock();
    msft.CurrentPrice = 30;
    msft.CurrentPrice -= 3;
    Console.WriteLine (msft.CurrentPrice);
    
    public class Stock
    {
        decimal currentPrice; // The private "backing" field
        public decimal CurrentPrice // The public property
        {
            get { return currentPrice; }
            set { currentPrice = value; }
        }
    }
    

    属性的 get ,set

    get/set 代表属性的访问器,get 访问器会在属性被读取的时候运行,必须返回一个该属性类型的值,set 访问器会在属性被赋值的时候运行,有一个隐式的该类型的参数 value,通常你会把 value 赋给一个私有字段。

    属性与字段的区别

    尽管属性的访问方式与字段的访问方式相同,但不同之处在于,属性赋予了实现者对获取和赋值的完全控制权。这种控制允许实现者选择任意所需的内部表示,不向属性的使用者公开其内部实现细节。

    只读和计算的属性

    如果属性只有 get 访问器,那么它是只读的,如果只有 set 访问器,那么它就是只写的(很少这样用)。属性通常拥有一个专用的“幕后”字段(backing field),这个幕后字段用来存储数据。

    decimal currentPrice, sharesOwned;
    public decimal Worth
    {
        get { return currentPrice * sharesOwned; }
    }
    

    Expression-bodied 属性

    从C# 6开始,你可以使用Expression-bodied形式来表示只读属性。public decimal Worth => currentPrice * sharesOwned;。C# 7,允许 set 访问器也可以使用该形式。

    public decimal Worth
    {
        get => currentPrice * sharesOwned;
        set => sharesOwned = value / currentPrice;
    }
    

    自动属性

    属性最常见的一种实现就是:getter 和 setter 只是对 private field 进行简单直接的读写,自动属性声明就告诉编译器来提供这种实现。编译器会自动生成一个私有的幕后字段,其名称不可引用(由编译器生成)。set 访问器也可以是 private 或 protected。

    public class Stock
    {
        ...
        public decimal CurrentPrice { get; set; }
    }
    

    属性初始化器

    从C# 6 开始,你可以为自动属性添加属性初始化器,public decimal CurrentPrice { get; set; } = 123;。只读的自动属性也可以使用(只读自动属性也可以在构造函数里被赋值),public int Maximum { get; } = 999;

    get 和 set 访问性

    get 和 set 访问器可以拥有不同的访问级别,典型用法:public get,internal/private set。注意,属性的访问级别更“宽松”一些,访问器的访问级别更“严”一些。

    public class Foo
    {
        private decimal x;
        public decimal X
        {
            get { return x; }
            private set { x = Math.Round (value, 2); }
        }
    }
    

    CLR的属性实现

    C# 的属性访问器内部会编译成 get_XXX 和 set_XXX。简单的非 virtual 属性访问器会被 JIT 编译器进行内联(inline)操作,这会消除访问属性与访问字段之间的性能差异。内联是一种优化技术,它会把方法调用换成直接使用方法体。

    public decimal get_CurrentPrice {...}
    public void set_CurrentPrice (decimal value) {...}
    

    索引器

    索引器提供了一种可以访问封装了列表值或字典值的 class/struct 的元素的一种自然的语法。语法很像使用数组时用的语法,但是这里的索引参数可以是任何类型的。

    string s = "hello";
    Console.WriteLine (s[0]); // 'h'
    Console.WriteLine (s[3]); // 'l'
    

    索引器和属性拥有同样的修饰符,可以按照下列方式使用null条件操作符:

    string s = null;
    Console.WriteLine (s?[0]); // Writes nothing; no error.
    

    实现索引器

    需要定义一个 this 属性,并通过中括号指定参数。

    class Sentence
    {
        string[] words = "The quick brown fox".Split();
        public string this [int wordNum] // indexer
        {
            get { return words [wordNum]; }
            set { words [wordNum] = value; }
        }
    }
    
    // 使用索引器
    Sentence s = new Sentence();
    Console.WriteLine (s[3]); // fox
    s[3] = "kangaroo";
    Console.WriteLine (s[3]); // kangaroo
    

    多个索引器

    一个类型可以声明多个索引器,它们的参数类型可以不同,一个索引器可以有多个参数。

    public string this [int arg1, string arg2]
    {
        get { ... } set { ... }
    }
    

    只读索引器

    如果不写 set 访问器,那么这个索引器就是只读的。在 C#6 以后,也可以使用 expression-bodied 语法。public string this [int wordNum] => words [wordNum];

    CLR的索引器实现

    索引器在内部会编译成 get_Item 和 set_Item 方法。

    public string get_Item (int wordNum) {...}
    public void set_Item (int wordNum, string value) {...}
    

    参考

    Object and Collection Initializers (C# Programming Guide)

    Properties (C# Programming Guide)

    Indexers (C# Programming Guide)

    C# - Object Initializer Syntax

    this (C# Reference)

    相关文章

      网友评论

          本文标题:C# 创建类型 03

          本文链接:https://www.haomeiwen.com/subject/cgbfjctx.html