用智能的编译器来防错

本章的主要内容:

  • 自动实现的属性:编写由字段直接支持的简单属性, 不再显得臃肿不堪;
  • 隐式类型的局部变量:根据初始值推断类型,简化局部变量的声明;
  • 对象和集合初始化程序:用一个表达式就能创建和初始化对象;
  • 隐式类型的数组:根据内容推断数组的类型,从而简化数组的创建过程;
  • 匿名类型:允许创建新的临时类型来包含简单的属性;

自动实现的属性

这个特性简单的我都不想描述,但是为了保持内容的完整性,放一张图:

和匿名方法还有迭代器一样,它在编译器的帮助下会生成一个后备字段。

自动实现的属性是赋值和取值方法都是共有的,当然你还可以继续使用C#2私有赋值方法。

在实现自己的结构(struct)时,所有构造函数都必须显式的调用一下无参的构造函数,只有这样,编译器才知道所有的字段都被明确赋值了。因为这里有个类型初始化的顺序:

类或结构在初始化时的执行顺序,依次如下:

1: 子类静态变量

2: 子类静态构造函数

3: 子类非静态变量

4: 父类静态变量

5: 父类静态构造函数

6: 父类非静态变量

7: 父类构造函数

8: 子类构造函数

可以看到除了构造函数以外其他东西都是先初始化本身在初始化基类的。

隐式类型的局部变量

首先需要说明一点的时隐式类型只能用于局部变量,不能用于字段变量。

第二点是,C#仍然是一门静态的语言,只是要求编译器为你来推断变量的类型,在编译时仍然是类型静态的。

用var关键字来声明一个隐式类型的局部变量。

小小的总结:不是在所有情况下都能为所有变量使用隐式类型, 只有在以下情况下才能用它:

  • 被声明的变量是一个局部变量, 而不是静态字段和实例字段;
  • 变量在声明的同时被初始化;
  • 初始化表达式不是方法组, 也不是匿名函数( 如果不进行强制类型转换);
  • 初始化表达式不是 null;
  • 语句中只声明了一个变量;
  • 你希望变量拥有的类型是初始化表达式的编译时类型;
  • 初始化表达式不包含正在声明的变量 。

隐式类型的局部变量也有一些不好的地方,有的时候你不得不仔细的判断它的类型。例如:

  • var a = 2147483647;
  • var b = 2147483648;
  • var c = 4294967295;
  • var d = 4294967296;
  • var e = 9223372036854775807;
  • var f = 9223372036854775808;

上面的这些变量的类型都不好在第一时间就判断出来。但是,有的时候你不得不用,比如要返回一个匿名的类型,只能这样写:var a=new {name="pangjianxin",age=10};这样的表达式你要用什么类型来引用呢?

简化的初始化

直接上代码。

public class Person
{
public string Name { get; }
public int Age { get; set; }
public List<Person> Persons { get; } = new List<Person>();
public Location Location { get; } = new Location();
public Person()
{ }
public Person(string name)
{
this.Name = name;
}
} public class Location
{
public string City { get; set; }
public string Street { get; set; }
}

上面定义两个类,一个Person类,一个Location类。Person类中维护两个自动属性Name和Age,另外,还维护了两个只读属性Persons和Location。还有一个无参的构造函数和一个有参数的构造函数。

前面已经说过,类会在静态的和非静态的字段初始化后才会执行构造函数,属性本质上来说是一对get/set方法,不存在初始化。

看一下调用情况:

 static void Main(string[] args)
{
Person p = new Person("pangianxin")
{
Age = ,
Location = { City = "baotou", Street = "gangtiedajie" }
};
Console.WriteLine(p.Location.City);
//p.Location=new Location();无法对Location进行初始化,因为他是只读的。

p.Location.City = "baotou ";
              p.Location.Street = "gangtiedajie ";

            Console.ReadKey();
}

上面使用了对象初始化程序来对对象进行初始化。

首先注意到的是p.Location是一个只读的属性。我们不能直接该给属性进行赋值,但是可以在取到这个属性的引用后,再对其进行赋值,在C#语言规范里面,这个叫做“设置一个嵌入对象的属性”。就是设置属性的属性。这样却没有了限制。

第二点是Location = { City = "baotou", Street = "gangtiedajie" }这句。编译器发现等号右侧的是另一个对象初始化程序, 所以会适当地将属性应用到嵌入对象。

集合初始化程序

var names = new List { "Holly", "Jon", "Tom", "Robin", "William" };

就是这样。

同样是编译器在后台调用add方法来将元素add进集合。

集合初始化程序必须要遵循以下两点:

  • 实现IEnumerale
  • 具有add方法

对于第一点来说,要求实现IEnumerable是合理的,因为编译器必须得知道是某种意义上的集合。对于第二点,因为编译器会在后台调用add方法来存放元素,所以,你初始化的这个集合必须得保证有这个add方法。

隐式类型的数组

string[] names = {"Holly", "Jon", "Tom", "Robin", "William"};

这种方式看起来很简洁,但是不能将大括号里面的东西直接传递给方法:MyMethod({" Holly", "Jon", "Tom", "Robin", "William"});这样会报错。要这样:MyMethod( new string[] {"Holly", "Jon", "Tom", "Robin", "William"});

匿名类型

这个玩意儿才是今天的重点。

匿名类型在与更高级的特性结合起来才会更有用。

用这个东西初始化的类型也只能用var来承接了:var myInfo=new {name="pangjianxin“,age=19};当然。除了object以外。

如果两个匿名对象初始化程序包含相同数量的属性, 且这些属性具有相同的名称和类型, 而且以相同的顺序出现, 就认为它们是同一个类型。

static void Main(string[] args)
{
var persons = new[]
{
new
{
name = "pangjianxin",
age =
},
new
{
name = "pangjianxin",
age =
},
new
{
name = "pangjianxin",
age =
},
new
{
name = "pangjianxin",
age =
},
new
{
name = "pangjianxin",
age =
}
};
Console.ReadKey();
}

如果上面的匿名类型的类型不一致,比如吧其中一个的属性的顺序颠倒,额外增加一个属性等等,编译器会报错:”找不到隐式类型数组的最佳类型“。

编译器在后台为匿名类型生成了一个泛型的类型来帮助匿名类型进行初始化,这个泛型类被放到了一个单独的程序集中。它在后台生成的类名超级变态:

它将匿名类型中的name和age的类型作为泛型类型的类型参数,然后,main方法中变成了这样:

编译器厉害吧?

返回来看一下匿名类型具有哪些成员:

首先它是继承了object。废话

它有后备字段和只读的属性

有获取所有初始值的构造函数。

重写了object的Equals、GetHashCode、ToString

由于所有属性都是只读的,所以只要这些属性是不易变的, 那么匿名类型就是不易变的。 这就为你提供了“ 不易变” 这一特性所具有全部常规性的优势—— 能放心向方法传递值, 不用害怕这些值被改变; 能在线程之间共享数据, 等等。

投影初始化程序

var person = new {name = "pangjianxin", age = 19};
var anotherPerson = new {name = person.name, isAdult = (person.age > 18)};

anotherPerson利用person的属性形成了一个新的匿名类型。这就是投影初始化程序。

不过匿名类型最大的用处在于linq中。利用select或selectmany等操作可以从横向的缩小要查找的范围。

C#复习笔记(4)--C#3:革新写代码的方式(用智能的编译器来防错)的更多相关文章

  1. C#复习笔记(4)--C#3:革新写代码的方式(Lambda表达式和表达式树)

    Lambda表达式和表达式树 先放一张委托转换的进化图 看一看到lambda简化了委托的使用. lambda可以隐式的转换成委托或者表达式树.转换成委托的话如下面的代码: Func<string ...

  2. C#复习笔记(4)--C#3:革新写代码的方式(查询表达式和LINQ to object(下))

    查询表达式和LINQ to object(下) 接下来我们要研究的大部分都会涉及到透明标识符 let子句和透明标识符 let子句不过是引入了一个新的范围变量.他的值是基于其他范围变量的.let 标识符 ...

  3. C#复习笔记(4)--C#3:革新写代码的方式(扩展方法)

    扩展方法 扩展方法有以下几个需求: 你想为一个类型添加一些 成员: 你不需要为类型的实例添加任何更多的数据: 你不能改变类型本身, 因为是别人的代码. 对于C#1和C#2中的静态方法,扩展方法是一种更 ...

  4. Java 10 的 10 个新特性,将彻底改变你写代码的方式!

    Java 9才发布几个月,很多玩意都没整明白,现在Java 10又快要来了.. 这时候我真尼玛想说:线上用的JDK 7 甚至JDK 6,JDK 8 还没用熟,JDK 9 才发布不久不知道啥玩意,JDK ...

  5. Java 10的10个新特性,将彻底改变你写代码的方式!

    Java 9才发布几个月,很多玩意都没整明白,现在Java 10又快要来了.. 这时候我真尼玛想说:线上用的JDK 7 甚至JDK 6,JDK 8 还没用熟,JDK 9 才发布不久不知道啥玩意,JDK ...

  6. Java 8 到 Java 14,改变了哪些你写代码的方式?

    前几天,JDK 14 正式发布了,这次发布的新版本一共包含了16个新的特性. 其实,从Java8 到 Java14 ,真正的改变了程序员写代码的方式的特性并不多,我们这篇文章就来看一下都有哪些. La ...

  7. Java基础复习笔记系列 八 多线程编程

    Java基础复习笔记系列之 多线程编程 参考地址: http://blog.csdn.net/xuweilinjijis/article/details/8878649 今天的故事,让我们从上面这个图 ...

  8. Java二次复习笔记(1)

    Java二次复习笔记(1) Java采用的默认字符编码集是Unicode. byte=byte+byte报错,值为int,而byte+=byte不报错.同样short = short+short报错, ...

  9. 斜率优化DP复习笔记

    前言 复习笔记2nd. Warning:鉴于摆渡车是普及组题目,本文的难度定位在普及+至省选-. 参照洛谷的题目难度评分(不过感觉部分有虚高,提高组建议全部掌握,普及组可以选择性阅读.) 引用部分(如 ...

随机推荐

  1. 【Teradata】tdlocaledef修改默认日期配置

    如下所有操作需要使用root登录到TD数据库节点操作 1.获取数据库当前默认配置 //使用root登录TD数据库节点 cd /opt/teradata/tdat/tdbms/xx.xx.xx.xx/b ...

  2. 前端性能优化成神之路--vue组件懒加载(Vue Lazy Component )

    ---恢复内容开始--- 使用组件懒加载的原因 我们先来看看这样的一个页面,页面由大量模块组成,所有模块是同时进行加载,模块中图片内容较多,每个模块的依赖资源较多(包括js文件.接口文件.css文件等 ...

  3. PHP服务器Apache与Nginx的对比分析

    PHP服务器Apache与Nginx的对比分析 本篇文章给大家带来的内容是关于PHP服务器Apache与Nginx的对比分析,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. Apach ...

  4. spring程序打包使用该插件,不然容易报错xsd找不到

    <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade ...

  5. 在flask中使用swagger(flasgger使用方法及效果展示)

    一. 部分代码及效果 from flask import Flask from flasgger import Swagger import config app = Flask(__name__) ...

  6. rabbitmq官方的六种工作模式

    1.RabbitMq1.1介绍RabbitMQ是一个消息代理:它接受并转发消息.你可以把它当成一个邮局:当你想邮寄信件的时候,你会把信件放在投递箱中,并确信邮递员最终会将信件送到收件人的手里.在这个例 ...

  7. 【转】从零开始玩转logback

    概述 LogBack是一个日志框架,它与Log4j可以说是同出一源,都出自Ceki Gülcü之手.(log4j的原型是早前由Ceki Gülcü贡献给Apache基金会的)下载地址:http://l ...

  8. Java内存模型(和堆栈等不是同一层次的划分)

    什么叫Java内存模型? 现代计算机通过指令的重排序来提升计算机的性能,而没有限制条件的指令重排序会使得程序的行为不可预测,JMM就是通过一系列的操作规则限制指令重排序的方式使得指令重排序不会破坏JM ...

  9. SkylineGlobe系列软件对机器配置要求

    6.6版本: TerraExplorer for Desktop / Web Operating System: Windows® 7/ 8/ 10 - 64 bit recommended Proc ...

  10. Python 中的 10 个常见安全漏洞,以及如何避免(上)

    简评:编写安全代码很困难,当你学习一个编程语言.模块或框架时,你会学习其使用方法. 在考虑安全性时,你需要考虑如何避免被滥用,Python 也不例外,即使在标准库中,也存在用于编写应用的不良实践.然而 ...