C#学习笔记四: C#3.0自动属性匿名属性及扩展方法

前言

这一章算是看这本书最大的收获了, Lambda表达式让人用着屡试不爽, C#3.0可谓颠覆了我们的代码编写风格. 因为Lambda所需篇幅挺大, 所以先总结C#3.0智能编译器给我们带来的诸多好处, 下一遍会单独介绍Lambda表达式. 这篇主要包括的内容有: 自动属性,隐式类型,对象集合初始化,匿名类型,扩展方法.

下面一起来看下C#3.0 所带来的变化吧.

1,自动实现的属性
在C#3.0之前, 定义属性时一般会像下面这样去编写代码:

 1 class Person
 2 {
 3     //定义私有字段
 4     private string name;
 5 
 6     //定义可读写属性
 7     public string Name
 8     {
 9         get{return name;}
10         set{name = value;}
11     }
12 
13     private int age;
14     //定义只读属性
15     public int Age
16     {
17         get{return age;}
18     }
19 }
View Code

PS: 这里有一个快捷键: private string name; 选中name 然后Ctrl + R + E 就可以快捷生成Name属性.

C#3.0之后, 对于不需要额外验证的属性(需要额外验证的属性还是必须采用之前的方式来定义), 我们可以使用自动实现的特性来对属性的定义进行简化, 此时不再需额外定义一个私有字段了.代码如下:

1 class Person
2 {
3     //使用自动实现的属性来定义属性
4     //定义可读写属性
5     public string Name{get; set;}
6     //定义只读属性
7     public int Age{get; private set;}
8 }
View Code

PS: 这里也有一个快捷键: 打出prop 然后点击两下Tab键就可以生成上面的属性了, 不过还需手动改值. 
类似的快捷键有: 输入cw 然后点击两下Tab键 就可以直接生成Console.WriteLine();了. 类似的还有很多, 在这里就不列举了.

之所以可以这样定义属性, 主要是因为编译器在编译时会为我们创建一个私有字段. 利用反编译工具可以知道使用自动实现的属性时,C#都会帮我们创建必要的字段.
另外在结构体中使用自动属性时, 需要注意的是所有构造函数都需要显式地调用无参构造函数this, 否则会出现编译错误. 因为只有这样,编译器才能知道所有的字段都已经被赋值.
然而, 类却不需要显式地调用无参构造函数, 这主要是由C#编译器的设计决定, 我们记住就好了.

 1 public struct TestPerson
 2 {
 3     public string Name { get; set; }
 4     public int Age { get; private set; }
 5 
 6     //结构体中, 不显式地调用无参构造函数this()时, 会出现编译时错误
 7     public TestPerson(string name)
 8     : this()
 9     {
10         this.Name = name;
11     }
12 }
View Code

2,隐式类型 var关键字
C#是强类型语言, 在定义一个变量时, 需要声明变量的类型. 然而类型的长度过长,就可能影响代码的可读写性. 为了避免这样的问题, C#3.0 引入了隐式类型,即可以使用关键字var来声明变量或数组.
var关键字告诉编译器去根据变量的值来推断其类型.

1 class Program
2 {
3     static void Main(stirng[] args)
4     {
5         //用var声明局部变量
6         var stringValue = "Barry Wang";
7         stringValue = 2;
8     }
9 }
View Code

这里就会报错"无法将类型int 隐式转换为string". 调试会发现stringValue是string类型的.
使用隐式类型有一些限制, 包括以下几点:
(1)被声明的变量是一个局部变量, 不能为字段
(2)变量在声明时必须被初始化, 因为编译器要根据变量的赋值来推断类型
(3)变量不能初始化为一个方法组, 也不能为一个匿名函数

3,对象集合初始化
在C#3.0之前定义类, 我们往往需要定义多个构造函数来完成不同情况下的初始化, C#3.0 提供了对象初始化器, 它减少了我们在类中定义的构造函数代码, 从而使代码更加简洁.

 1 class Program
 2 {
 3     static void Main(string[] args)
 4     { 
 5         //没有对象初始化器时的代码
 6         Person p = new Person("BarryWang", 25);
 7         p.Weight = 60;
 8         p.Height = 170;
 9     }
10 }
11 
12 public class Person
13 {
14     public string Name { get; set; }
15     public int Age { get; set; }
16     public int Weight { get; set; }
17     public int Height { get; set; }
18 
19     //定义不同情况下的初始化函数
20     public Person() { }
21     public Person(string name) { } 
22     public Person(string name, int age) { }
23     public Person(string name, int age, int weight) { }
24     public Person(string name, int age, int weight, int height) 
25     {
26         this.Name = name;
27         this.Age = age;
28         this.Weight = weight;
29         this.Height = height;
30     }
31 }
View Code

在C#3.0引入对象初始化之后, 我们就不需要定义多个构造函数了, 前面的代码可以简化为如下的形式:

 1 class Program
 2 {
 3     static void Main(string[] args)
 4     {
 5         //使用对象初始化器初始化
 6         Person p = new Person() {Name = "Barry Wang", Age = 25, Weght = 60, Height = 70};
 7         //下面这行代码和上面一行是等价的, 只不过下面省略了构造函数的圆括号而已
 8         Person p2 = new Person {Name = "Barry Wang", Age = 25, Weght = 60, Height = 70};
 9     }
10 }
11 
12 public class Person
13 {
14     public string Name { get; set; }
15     public int Age { get; set; }
16     public int Weight { get; set; }
17     public int Height { get; set; }
18 }
View Code

利用反编译工具可以知道编译器为对象做了如下处理: 首先C#编译器生成了一个Person类的临时对象, 并调用Person类的默认无参构造函数对其初始化.然后对它的属性逐个赋值.
由此可以想到,要使用对象初始化器,则必须保证类中具有一个无参构造函数. 如果我们自定义了一个有参构造函数而把默认的无参构造函数覆盖了, 则需要重新定义一个无参构造函数.

再例如 给List 中添加元素, 在C#3.0 之前我们需要一个个Add 添加, 而现在直接可以利用集合初始化器即可, 编译器会调用Add方法, 一个个地将初始化的内容添加进去.

1 class Program
2 {
3     List<string> newNames = new List<string>
4     {
5         "A", "B", "C"
6     };
7 }
View Code

4,匿名类型
匿名类型顾名思义就是没有知名类型的类型, 通过隐式类型和对象初始化器两种特性创建了一个类型未知的对象, 使我们在不定义类型的情况下可以实现对象的创建,从而减少了类定义过程的代码.

 1 class Program
 2 {
 3     static void Main(string[] args)
 4     { 
 5         //定义匿名类型对象
 6         var person = new {Name = "Barry Wang", Age = 25 };
 7         Console.WriteLine("{0} 的年龄为: {1}", person.Name, person.Age);
 8 
 9         //定义匿名类型数组
10         var personCollection = new[]
11         {
12             new {Name = "Barry", Age = 30},
13             new {Name = "Brad", Age = 22},
14             new {Name = "Tony", Age = 25}
15         };
16 
17         int totalAge = 0;
18         foreach (var p in personCollection)
19         {
20             //下面一行代码证明了Age属性时int类型
21             totalAge += p.Age;
22         }
23 
24         Console.WriteLine("所有人的年龄总和为: {0}", totalAge);
25         Console.ReadKey();
26     }
27 }
View Code

5,扩展方法
扩展方法, 首先是一个方法, 他可以用来扩展已定义类型中的方法成员.
如下例所示:

 1 public static class Extensions
 2 {
 3     //对string 类扩展,注意this关键字
 4     public static void TestStr(this string s)
 5     {
 6         Console.WriteLine(s.ToString());
 7     }
 8     //对int 类扩展
 9     public static void TestInt(this int i, string s)
10     {
11         Console.WriteLine(s + i.ToString());
12     }
13 }
14 class Program
15 {
16     static void Main(string[] args)
17     {
18         //使用扩展方法
19         string s = "Test Extensions Methods";
20         s.TestStr();//调用方法1
21         Extensions.TestStr(s);//调用方法2
22         int i = 100;
23         i.TestInt("This value is ");
24         Extensions.TestInt(i, "This value is ");
25         Console.ReadKey();
26     }
27 }
View Code

并不是所有的方法都可以用作扩展方法, 我们需要考察它是否符合下列扩展方法的定义规则:
(1)扩展方法必须在一个非嵌套, 非泛型的静态类中定义
(2)它至少要有一个参数
(3)第一个参数必须加上this关键字作为前缀(第一个参数类型也称为扩展类型, 即指方法对这个类型进行扩展)
(4)第一个参数不能使用任何其他修饰符(如不能使用ref out等)

 1 namespace CurrentNameSpace
 2 {
 3     //要想使用不用命名空间下的扩展方法, 需要先引入该命名空间
 4     using CustomNameSpace;
 5     class Program
 6     {
 7         static void Main(string[] args)
 8         {
 9             Person p = new Person {Name = "BarryWang"};
10             p.Print();//等价于==>ExtensionClass.Print(p);
11             p.Print("Hello");
12             Console.ReadKey();
13         }
14     }
15 
16     //自定义类型
17     public class Person
18     {
19         public string Name { get; set; }
20     }
21 
22     //当前命名空间下的扩展方法定义
23     public static class ExtensionClass
24     {
25         public static void Print(this Person p)
26         {
27             Console.WriteLine("调用的是当前命名空间下的扩展方法输出, 姓名为: {0}", p.Name);
28         }
29     }
30 }
31 
32 namespace CustomNameSpace
33 {
34     using CurrentNameSpace;
35     public static class CustomExtensionClass
36     {
37         //扩展方法定义
38         public static void Pint(this Person p)
39         {
40             Console.WriteLine("调用的是CustomNameSpace命名空间下扩展方法输出: 姓名为: {0}", p.Name);
41         }
42 
43         //扩展方法定义
44         public static void Pint(this Person p, string s)
45         {
46             Console.WriteLine("调用的是CustomNameSpace命名空间下扩展方法输出: 姓名为: {0},附加字符串为{1}", p.Name, s);
47         }
48     }
49 }
View Code

打印结果是:
调用的是当前命名空间下的扩展方法输出, 姓名为: Barry Wang
调用的是CustomNameSpace命名空间下扩展方法输出, 姓名为: Barry Wang, 附加字符串为Hello

注解: 大家看到p.Print(); 是否有些疑惑呢? 对于C#3.0编译器而言, 当它看到某个类型的变量在调用方法时, 它会首先去该对象的实例方法进行查找,如果没有找到与调用方法同名并参数一致的实例方法,
编译器就回去查找是否存在合适的扩展方法. 编译器会检查所有导入的命名空间和当前命名空间中的扩展方法, 并将变量类型匹配到扩展类型.
从编译器发现扩展方法的过程来看, 方法调用的优先级别顺序为: 类型的实例方法-->当前命名空间下的扩展方法-->导入命名空间的扩展方法.

解释上面代码打印结果的由来: 在以上代码中存在另个不同的命名空间, 它们都定义了带一个参数的扩展方法Print. 根据执行顺序, 编译器会首先查看Person类型中是否定义了无参的Print实例方法.
如果有, 则停止查找; 否则继续查找当前命名空间下, 即CurrentNameSpace下是否定义了一个带参数的扩展方法Print.
此时在CurrentNameSpace命名空间下, 正好存在带一个扩展类型参数的Print扩展方法, 编译器因此不会再继续查找其他引入命名空间了. 所以p.Print() 调用的是当前命名空间下的Print扩展方法.
而p.Print("Hello")的调用也是一个道理.

转载于:https://www.cnblogs.com/xinhuawei/p/5409275.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/396240.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mongdb集群3.4 shard 模式

从图中可以看到有四个组件&#xff1a;mongos、config server、shard、replica set。mongos&#xff1a;数据库集群请求的入口&#xff0c;所有的请求都通过mongos进行协调&#xff0c;不需要在应用程序添加一个路由选择器&#xff0c;mongos自己就是一个请求分发中心&#xff…

alpha值计算 qcolor_量化交易与机器学习(四):如何研究alpha因子

算法交易策略由指示何时购买或出售资产以产生相对于基准&#xff08;例如指数&#xff09;的较高回报的信号驱动。 资产回报率中未通过暴露于该基准而无法解释的部分称为alpha&#xff0c;因此旨在产生这种不相关收益的信号也称为alpha因子。本章主要介绍alpha因子一、从数据到…

项目启动及需求分析(靳嘉豪、胡新宇、李晨曦、杨航、李瑶)团队作业

&#xff08;1&#xff09; 这次团队我们给我们团队起的名字是&#xff1a;桥上吊刀刀倒吊着 队员分别为&#xff1a;靳嘉豪、胡新宇、李晨曦、李瑶、杨航。 队训为&#xff1a;黑化肥挥发发灰会挥发。 胡新宇&#xff1a;http://www.cnblogs.com/hxy94264/ 靳嘉豪&#xff1a;…

20155229 实验一《Java开发环境的熟悉》实验报告

20155229 实验一《Java开发环境的熟悉》实验报告 实验内容 1.使用JDK编译、运行简单的Java程序&#xff1b; 2.使用Idea 编辑、编译、运行、调试Java程序。 实验步骤 &#xff08;一&#xff09;命令行下Java程序开发 输入 mkdir 20155229命令建立实验目录&#xff0c;用ls查看…

PHP代码20个实用技巧(转)

这些技巧特别是封装的&#xff0c;相对路径的还是挺好的&#xff0c;本身来自微信公众号&#xff0c;但是我担心以后删除&#xff0c;所以在我的博客上备份一下&#xff08;微信公众号为:菜鸟教程&#xff09; 在这篇文章中我们将看看一些关于PHP开发有用的提示和技巧&#xff…

python基础数据类型的相关知识点

1、字符串的函数join >>> s "Hello" >>> s1 s.join("你好")#将字符串Hello插入到你好中 >>> s1 你Hello好 >>> s2 "Tanxu".join("你好吗")#将字符串Tanxu插入到你好吗中 >>> s2 你Ta…

最长无重复字符子串?

2019独角兽企业重金招聘Python工程师标准>>> 题目要求&#xff1a; 给定一个字符串S&#xff0c;在该字符串中找到一个最长的没有重复字符的子串。 转载于:https://my.oschina.net/datacube/blog/875545

selenium框架安装及webdriver安装

本文介绍的是selenium安装及webdriver安装、小实例 1、selenium介绍 selenium是一个用于web应用程序测试的工具。 Selenium测试直接运行在浏览器&#xff0c;就向真正的用户操作一样。 支持的浏览器包括IE(7,8,9,10,11),Mazilla Firefox,Safari,Google Chrome,OperaL浏览器 这个…

突然想到了王自如

刚刚不知道为什么突然想到了王自如。可能是因为下午在腾讯视频首页看到了老罗罗永浩的一个访谈节目&#xff0c;然后神经元一短路的原因吧。 想到王自如不禁又联想到了王自如和罗永浩的那场著名的撕逼之战。场面上王自如是被罗老师教做人的一个结果。然后就很长时间没有听到关于…

UOJ Test Round 3

A.几何冲刺 感觉自己的智商爆炸。 显然是按照极角序排列之后依次加点&#xff0c;判断是否有点。 保证一个点在两个角的范围内就OK了啊&#xff0c;想了半天叉积。。。 #include "triangles.h" #include <bits/stdc.h> #define for1(a,b,i) for(int ia,end_b;i…

PPP认证方式pap chap chap2

2019独角兽企业重金招聘Python工程师标准>>> PPP点到点协议&#xff08;Point to Point Protocol&#xff0c;PPP&#xff09;是IETF&#xff08;Internet Engineering Task Force&#xff0c;因特网工程任务组&#xff09;推出的点到点类型线路的数据链路层协议。它…

Nexus-配置vPC 实验三

配置EvPC&#xff08;增强的vPC&#xff09;&#xff0c;下面两个FEX可以同时被两个N5K管理。注意&#xff1a;FEX只支持静态的Channel-group&#xff08;mode on&#xff09; N5K-1配置&#xff1a;配置FEXN5K-1&#xff08;config&#xff09;#feature fexN5K-1&#xff08;c…

python现在时间 命令,Python 日期格式和时间以及当前时间和时间戳

Python 程序在运行的时候可能需要获得当前的时间。在这个时候我们需要导入 datetime 包。获得当前时间例如&#xff0c;可以使用下面的代码获得当前的日期。today datetime.date.today()print("Todays date:", today)在上面的代码中&#xff0c;将会输出&#xff1a…

go grpc 深入笔记

为什么80%的码农都做不了架构师&#xff1f;>>> grpc 深入 生命周期 grpc 的生命周期由4种请求的方式不同而不同&#xff1a;(详细查看router示例) 普通rpc: 客户端发送请求&#xff0c;通知服务端调用rpc服务&#xff0c;服务端返回请求&#xff0c;如果状态"…

RSA加密算法简单分析

预备知识 1&#xff09;RSA是第一个比较完善的公开密钥算法&#xff0c;它既能用于加密&#xff0c;也能用于数字签名。RSA以它的三个发明者Ron Rivest, Adi Shamir, Leonard Adleman的名字首字母命名&#xff0c;这个算法经受住了多年深入的密码分析&#xff0c;虽然密码分析者…

Linux 小笔记

1、查看linux 版本 按ctrlshiftt 快捷键&#xff0c;打开终端&#xff0c;输入sudo uname --m &#xff0c;按下enter 如果显示i686,你安装了32位操作系统 如果显示 x86_64&#xff0c;你安装了64位操作系统 转载于:https://www.cnblogs.com/1995hxt/p/5436683.html

不会发布npm包?进来看看?

前言 npm(Node Package Manager)&#xff0c;一个Node的包管理器&#xff0c;平时我们常用的公共模块&#xff08;插件&#xff09;或者叫做包大多都放在上面&#xff0c;所以接下来要封装的插件&#xff0c;我们就简单称它为npm包&#xff0c;本文从就从这个简单的例子开始&am…

Nova 组件详解 - 每天5分钟玩转 OpenStack(26)

本节开始&#xff0c;我们将详细讲解 Nova 的各个子服务。 前面架构概览一节知道 Nova 有若干 nova-* 的子服务&#xff0c;下面我们将依次学习最重要的几个。今天先讨论 nova-api 和 nova-conductor。 nova-api Nova-api 是整个 Nova 组件的门户&#xff0c;所有对 Nova 的请…

12_04_Linux软件管理之四yum

2019独角兽企业重金招聘Python工程师标准>>> RPM安装&#xff1a; 二进制格式&#xff1a; 源程序--》编译--》二进制格式 有些特性是编译时选定的&#xff0c;如果编译时未选定此特性&#xff0c;将无法使用&#xff1b; rpm包的版本会落后于源码包&#xff0c;甚至…

linux 解析elf文件格式,Linux下ELF文件解析

1. windows PE文件与Linux ELF文件概述在windows中可执行文件是pe文件格式&#xff0c;Linux中可执行文件是ELF文件&#xff0c;其文件格式是ELF文件格式&#xff0c;在Linux下的ELF文件除了可执行文件(Excutable File),可重定位目标文件(RellocatableObject File)、共享目标文…