C#复习笔记(4)--C#3:革新写代码的方式(用智能的编译器来防错)
用智能的编译器来防错
本章的主要内容:
- 自动实现的属性:编写由字段直接支持的简单属性, 不再显得臃肿不堪;
- 隐式类型的局部变量:根据初始值推断类型,简化局部变量的声明;
- 对象和集合初始化程序:用一个表达式就能创建和初始化对象;
- 隐式类型的数组:根据内容推断数组的类型,从而简化数组的创建过程;
- 匿名类型:允许创建新的临时类型来包含简单的属性;
自动实现的属性
这个特性简单的我都不想描述,但是为了保持内容的完整性,放一张图:

和匿名方法还有迭代器一样,它在编译器的帮助下会生成一个后备字段。
自动实现的属性是赋值和取值方法都是共有的,当然你还可以继续使用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:革新写代码的方式(用智能的编译器来防错)的更多相关文章
- C#复习笔记(4)--C#3:革新写代码的方式(Lambda表达式和表达式树)
Lambda表达式和表达式树 先放一张委托转换的进化图 看一看到lambda简化了委托的使用. lambda可以隐式的转换成委托或者表达式树.转换成委托的话如下面的代码: Func<string ...
- C#复习笔记(4)--C#3:革新写代码的方式(查询表达式和LINQ to object(下))
查询表达式和LINQ to object(下) 接下来我们要研究的大部分都会涉及到透明标识符 let子句和透明标识符 let子句不过是引入了一个新的范围变量.他的值是基于其他范围变量的.let 标识符 ...
- C#复习笔记(4)--C#3:革新写代码的方式(扩展方法)
扩展方法 扩展方法有以下几个需求: 你想为一个类型添加一些 成员: 你不需要为类型的实例添加任何更多的数据: 你不能改变类型本身, 因为是别人的代码. 对于C#1和C#2中的静态方法,扩展方法是一种更 ...
- Java 10 的 10 个新特性,将彻底改变你写代码的方式!
Java 9才发布几个月,很多玩意都没整明白,现在Java 10又快要来了.. 这时候我真尼玛想说:线上用的JDK 7 甚至JDK 6,JDK 8 还没用熟,JDK 9 才发布不久不知道啥玩意,JDK ...
- Java 10的10个新特性,将彻底改变你写代码的方式!
Java 9才发布几个月,很多玩意都没整明白,现在Java 10又快要来了.. 这时候我真尼玛想说:线上用的JDK 7 甚至JDK 6,JDK 8 还没用熟,JDK 9 才发布不久不知道啥玩意,JDK ...
- Java 8 到 Java 14,改变了哪些你写代码的方式?
前几天,JDK 14 正式发布了,这次发布的新版本一共包含了16个新的特性. 其实,从Java8 到 Java14 ,真正的改变了程序员写代码的方式的特性并不多,我们这篇文章就来看一下都有哪些. La ...
- Java基础复习笔记系列 八 多线程编程
Java基础复习笔记系列之 多线程编程 参考地址: http://blog.csdn.net/xuweilinjijis/article/details/8878649 今天的故事,让我们从上面这个图 ...
- Java二次复习笔记(1)
Java二次复习笔记(1) Java采用的默认字符编码集是Unicode. byte=byte+byte报错,值为int,而byte+=byte不报错.同样short = short+short报错, ...
- 斜率优化DP复习笔记
前言 复习笔记2nd. Warning:鉴于摆渡车是普及组题目,本文的难度定位在普及+至省选-. 参照洛谷的题目难度评分(不过感觉部分有虚高,提高组建议全部掌握,普及组可以选择性阅读.) 引用部分(如 ...
随机推荐
- 【大数据技术】Sqoop
1.Sqoop是什么 Sqoop:SQL-to-Hadoop,传统数据库与Hadoop间数据同步工具.(MySQL.Oracle <==> HDFS.HBase.Hive) Sqoop ...
- 解决:Vue刷新/载入页面,出现双括号闪现后消失
https://cn.vuejs.org/v2/api/#v-cloak v-cloak 不需要表达式 用法: 这个指令保持在元素上直到关联实例结束编译.和 CSS 规则如 [v-cloak] { d ...
- (转)springcloud(一):大话Spring Cloud
http://www.ityouknow.com/springcloud/2017/05/01/simple-springcloud.html 研究了一段时间Spring Boot了准备向Spring ...
- 「AHOI / HNOI2017」影魔
「AHOI / HNOI2017」影魔 题目描述 解决这类比较复杂的区间贡献问题关键在于找到计算的对象. 比如这道题,我们计算的对象就是区间中间的最大值. 对于点\(i\),我们找到左边第一个比他大的 ...
- python六十九课——网络编程之TCP协议
1.1 概述: TCP协议通过三次握手协议将客户端与服务器端连接,两端使用各自的Socket对象.Socket对象中包含了IO流,供数据传输. 即:TCP协议在客户端与服务器端通过Socket组成了I ...
- Android中实现短信发送的一种方式
SendSmsActivity.java: package com.test.smsmangerdemo.sendsmsactivity; import android.support.v7.app. ...
- Gps定位和wifi定位和基站定位的比较
现在手机定位的方式是:Gps定位,wifi定位,基站定位 Gps定位的前提,手机开启Gps定位模块,在室外,定位的精度一般是几米的范围 wifi定位的前提,手机要开启wifi,连不连上wifi热点都可 ...
- UVA10817-Headmaster's Headache(动态规划基础)
Problem UVA10817-Headmaster's Headache Time Limit: 4500 mSec Problem Description Input The input con ...
- Python中的单例模式——装饰器实现剖析
Python中单例模式的实现方法有多种,但在这些方法中属装饰器版本用的广,因为装饰器是基于面向切面编程思想来实现的,具有很高的解耦性和灵活性. 单例模式定义:具有该模式的类只能生成一个实例对象. 先将 ...
- 【angularjs】使用ionic+angular 搭建移动端项目,字体适配
解析: 首先,rem是以html为基准. 一般的,各大主流浏览器的font-size默认值为16px,此时1rem=16px.如果此时将rem与px进行换算很麻烦,比如0.75rem=12px. 为了 ...