[读书笔记]C#学习笔记五: C#3.0自动属性,匿名属性及扩展方法
前言
这一章算是看这本书最大的收获了, Lambda表达式让人用着屡试不爽, C#3.0可谓颠覆了我们的代码编写风格. 因为Lambda所需篇幅挺大, 所以先总结C#3.0智能编译器给我们带来的诸多好处, 下一遍会单独介绍Lambda表达式. 这篇主要包括的内容有: 自动属性,隐式类型,对象集合初始化,匿名类型,扩展方法.
下面一起来看下C#3.0 所带来的变化吧.
1,自动实现的属性
在C#3.0之前, 定义属性时一般会像下面这样去编写代码:
class Person
{
//定义私有字段
private string name; //定义可读写属性
public string Name
{
get{return name;}
set{name = value;}
} private int age;
//定义只读属性
public int Age
{
get{return age;}
}
}
PS: 这里有一个快捷键: private string name; 选中name 然后Ctrl + R + E 就可以快捷生成Name属性.
C#3.0之后, 对于不需要额外验证的属性(需要额外验证的属性还是必须采用之前的方式来定义), 我们可以使用自动实现的特性来对属性的定义进行简化, 此时不再需额外定义一个私有字段了.代码如下:
class Person
{
//使用自动实现的属性来定义属性
//定义可读写属性
public string Name{get; set;}
//定义只读属性
public int Age{get; private set;}
}
PS: 这里也有一个快捷键: 打出prop 然后点击两下Tab键就可以生成上面的属性了, 不过还需手动改值.
类似的快捷键有: 输入cw 然后点击两下Tab键 就可以直接生成Console.WriteLine();了. 类似的还有很多, 在这里就不列举了.
之所以可以这样定义属性, 主要是因为编译器在编译时会为我们创建一个私有字段. 利用反编译工具可以知道使用自动实现的属性时,C#都会帮我们创建必要的字段.
另外在结构体中使用自动属性时, 需要注意的是所有构造函数都需要显式地调用无参构造函数this, 否则会出现编译错误. 因为只有这样,编译器才能知道所有的字段都已经被赋值.
然而, 类却不需要显式地调用无参构造函数, 这主要是由C#编译器的设计决定, 我们记住就好了.
public struct TestPerson
{
public string Name { get; set; }
public int Age { get; private set; } //结构体中, 不显式地调用无参构造函数this()时, 会出现编译时错误
public TestPerson(string name)
: this()
{
this.Name = name;
}
}
2,隐式类型 var关键字
C#是强类型语言, 在定义一个变量时, 需要声明变量的类型. 然而类型的长度过长,就可能影响代码的可读写性. 为了避免这样的问题, C#3.0 引入了隐式类型,即可以使用关键字var来声明变量或数组.
var关键字告诉编译器去根据变量的值来推断其类型.
class Program
{
static void Main(stirng[] args)
{
//用var声明局部变量
var stringValue = "Barry Wang";
stringValue = ;
}
}
这里就会报错"无法将类型int 隐式转换为string". 调试会发现stringValue是string类型的.
使用隐式类型有一些限制, 包括以下几点:
(1)被声明的变量是一个局部变量, 不能为字段
(2)变量在声明时必须被初始化, 因为编译器要根据变量的赋值来推断类型
(3)变量不能初始化为一个方法组, 也不能为一个匿名函数
3,对象集合初始化
在C#3.0之前定义类, 我们往往需要定义多个构造函数来完成不同情况下的初始化, C#3.0 提供了对象初始化器, 它减少了我们在类中定义的构造函数代码, 从而使代码更加简洁.
class Program
{
static void Main(string[] args)
{
//没有对象初始化器时的代码
Person p = new Person("BarryWang", );
p.Weight = ;
p.Height = ;
}
} public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public int Weight { get; set; }
public int Height { get; set; } //定义不同情况下的初始化函数
public Person() { }
public Person(string name) { }
public Person(string name, int age) { }
public Person(string name, int age, int weight) { }
public Person(string name, int age, int weight, int height)
{
this.Name = name;
this.Age = age;
this.Weight = weight;
this.Height = height;
}
}
在C#3.0引入对象初始化之后, 我们就不需要定义多个构造函数了, 前面的代码可以简化为如下的形式:
class Program
{
static void Main(string[] args)
{
//使用对象初始化器初始化
Person p = new Person() {Name = "Barry Wang", Age = , Weght = , Height = };
//下面这行代码和上面一行是等价的, 只不过下面省略了构造函数的圆括号而已
Person p2 = new Person {Name = "Barry Wang", Age = , Weght = , Height = };
}
} public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public int Weight { get; set; }
public int Height { get; set; }
}
利用反编译工具可以知道编译器为对象做了如下处理: 首先C#编译器生成了一个Person类的临时对象, 并调用Person类的默认无参构造函数对其初始化.然后对它的属性逐个赋值.
由此可以想到,要使用对象初始化器,则必须保证类中具有一个无参构造函数. 如果我们自定义了一个有参构造函数而把默认的无参构造函数覆盖了, 则需要重新定义一个无参构造函数.
再例如 给List 中添加元素, 在C#3.0 之前我们需要一个个Add 添加, 而现在直接可以利用集合初始化器即可, 编译器会调用Add方法, 一个个地将初始化的内容添加进去.
class Program
{
List<string> newNames = new List<string>
{
"A", "B", "C"
};
}
4,匿名类型
匿名类型顾名思义就是没有知名类型的类型, 通过隐式类型和对象初始化器两种特性创建了一个类型未知的对象, 使我们在不定义类型的情况下可以实现对象的创建,从而减少了类定义过程的代码.
class Program
{
static void Main(string[] args)
{
//定义匿名类型对象
var person = new {Name = "Barry Wang", Age = };
Console.WriteLine("{0} 的年龄为: {1}", person.Name, person.Age); //定义匿名类型数组
var personCollection = new[]
{
new {Name = "Barry", Age = },
new {Name = "Brad", Age = },
new {Name = "Tony", Age = }
}; int totalAge = ;
foreach (var p in personCollection)
{
//下面一行代码证明了Age属性时int类型
totalAge += p.Age;
} Console.WriteLine("所有人的年龄总和为: {0}", totalAge);
Console.ReadKey();
}
}
5,扩展方法
扩展方法, 首先是一个方法, 他可以用来扩展已定义类型中的方法成员.
如下例所示:
public static class Extensions
{
//对string 类扩展,注意this关键字
public static void TestStr(this string s)
{
Console.WriteLine(s.ToString());
}
//对int 类扩展
public static void TestInt(this int i, string s)
{
Console.WriteLine(s + i.ToString());
}
}
class Program
{
static void Main(string[] args)
{
//使用扩展方法
string s = "Test Extensions Methods";
s.TestStr();//调用方法1
Extensions.TestStr(s);//调用方法2
int i = ;
i.TestInt("This value is ");
Extensions.TestInt(i, "This value is ");
Console.ReadKey();
}
}
并不是所有的方法都可以用作扩展方法, 我们需要考察它是否符合下列扩展方法的定义规则:
(1)扩展方法必须在一个非嵌套, 非泛型的静态类中定义
(2)它至少要有一个参数
(3)第一个参数必须加上this关键字作为前缀(第一个参数类型也称为扩展类型, 即指方法对这个类型进行扩展)
(4)第一个参数不能使用任何其他修饰符(如不能使用ref out等)
namespace CurrentNameSpace
{
//要想使用不用命名空间下的扩展方法, 需要先引入该命名空间
using CustomNameSpace;
class Program
{
static void Main(string[] args)
{
Person p = new Person {Name = "BarryWang"};
p.Print();//等价于==>ExtensionClass.Print(p);
p.Print("Hello");
Console.ReadKey();
}
} //自定义类型
public class Person
{
public string Name { get; set; }
} //当前命名空间下的扩展方法定义
public static class ExtensionClass
{
public static void Print(this Person p)
{
Console.WriteLine("调用的是当前命名空间下的扩展方法输出, 姓名为: {0}", p.Name);
}
}
} namespace CustomNameSpace
{
using CurrentNameSpace;
public static class CustomExtensionClass
{
//扩展方法定义
public static void Pint(this Person p)
{
Console.WriteLine("调用的是CustomNameSpace命名空间下扩展方法输出: 姓名为: {0}", p.Name);
} //扩展方法定义
public static void Pint(this Person p, string s)
{
Console.WriteLine("调用的是CustomNameSpace命名空间下扩展方法输出: 姓名为: {0},附加字符串为{1}", p.Name, s);
}
}
}
打印结果是:
调用的是当前命名空间下的扩展方法输出, 姓名为: Barry Wang
调用的是CustomNameSpace命名空间下扩展方法输出, 姓名为: Barry Wang, 附加字符串为Hello
注解: 大家看到p.Print(); 是否有些疑惑呢? 对于C#3.0编译器而言, 当它看到某个类型的变量在调用方法时, 它会首先去该对象的实例方法进行chazhao,如果没有找到与调用方法同名并参数一致的实例方法,
编译器就回去查找是否存在合适的扩展方法. 编译器会检查所有导入的命名空间和当前命名空间中的扩展方法, 并将变量类型匹配到扩展类型.
从编译器发现扩展方法的过程来看, 方法调用的优先级别顺序为: 类型的实例方法-->当前命名空间下的扩展方法-->导入命名空间的扩展方法.
解释上面代码打印结果的由来: 在以上代码中存在另个不同的命名空间, 它们都定义了带一个参数的扩展方法Print. 根据执行顺序, 编译器会首先查看Person类型中是否定义了无参的Print实例方法.
如果有, 则停止查找; 否则继续查找当前命名空间下, 即CurrentNameSpace下是否定义了一个带参数的扩展方法Print.
此时在CurrentNameSpace命名空间下, 正好存在带一个扩展类型参数的Print扩展方法, 编译器因此不会再继续查找其他引入命名空间了. 所以p.Print() 调用的是当前命名空间下的Print扩展方法.
而p.Print("Hello")的调用也是一个道理.
PS: 终于把今天的两篇博文更新完了, 这么冷的天 真是不容易, 记录到这里希望自己以后有时间还会再看看吧!
[读书笔记]C#学习笔记五: C#3.0自动属性,匿名属性及扩展方法的更多相关文章
- [读书笔记]C#学习笔记三: C#类型详解..
前言 这次分享的主要内容有五个, 分别是值类型和引用类型, 装箱与拆箱,常量与变量,运算符重载,static字段和static构造函数. 后期的分享会针对于C#2.0 3.0 4.0 等新特性进行. ...
- [读书笔记]C#学习笔记一: .Net Framwork
前言: 一次偶然的机会 在园子里看到@Learning hard 出版的一本书: <<C#学习笔记>>, 然后买来 一直到现在读完, 感觉很不错, 适合入门, 书中内容是从C ...
- [读书笔记]C#学习笔记八:StringBuilder与String详解及参数传递问题剖析
前言 上次在公司开会时有同事分享windebug的知识, 拿的是string字符串Concat拼接 然后用while(true){}死循环的Demo来讲解.其中有提及string操作大量字符串效率低下 ...
- [读书笔记]C#学习笔记四: C#2.0泛型 可控类型 匿名方法和迭代器
前言 C#1.0的委托特性使方法作为其他方法的参数来传递,而C#2.0 中提出的泛型特性则使类型可以被参数化,从而不必再为不同的类型提供特殊版本的实现方法.另外C#2.0还提出了可空类型,匿名方法和迭 ...
- [读书笔记]C#学习笔记二: 委托和事件的用法及不同.
前言: C#委托是什么 c#中的委托可以理解为函数的一个包装, 它使得C#中的函数可以作为参数来被传递, 这在作用上相当于C++中的函数指针. C++用函数指针获取函数的入口地址, 然后通过这个指针 ...
- [读书笔记]C#学习笔记七: C#4.0中微小改动-可选参数,泛型的可变性
前言 下面就开始总结C#4.0的一些变化了, 也是这本书中最后的一点内容了, 这一部分终于要更新完了. 同时感觉再来读第二遍也有不一样的收获. 今天很嗨的是武汉下雪了,明天周六,一切都是这么美好.哈哈 ...
- [读书笔记]C#学习笔记六: C#3.0Lambda表达式及Linq解析
前言 最早使用到Lambda表达式是因为一个需求:如果一个数组是:int[] s = new int[]{1,3,5,9,14,16,22};例如只想要这个数组中小于15的元素然后重新组装成一个数组或 ...
- Asp.net MVC4高级编程学习笔记-模型学习第五课MVC表单和HTML辅助方法20171101
MVC表单和HTML辅助方法 一.表单的使用. 表单中的action与method特性.Action表示表单要提交往那里,因此这里就有一个URL.这个URL可以是相对或绝对地址.表单默认的method ...
- 面向小白的JS笔记 - #Codecademy#学习笔记
前言 最初浏览过<JavaScript秘密花园>,前一段时间读过一点点<JavaScript语言精粹>和一点点<JavaScript高级程序设计>(一点点是指都只是 ...
随机推荐
- 自定义UIPageControl,可设置任意图片image。
[self.pageControl setValue:[UIImage imageNamed:@"选中图片名称"] forKeyPath:@"_currentPageIm ...
- android里R.layout.的问题
今天,在Exlipse里的一个项目在.java文件里写 setContentView(R.layout.activity_problem);时,显示错误,以为是R.java文件里没有对应的activ ...
- 利用ASP.NET加密和解密Web.config中连接字符串
摘自:博客园 介绍 这篇文章我将介绍如何利用ASP.NET来加密和解密Web.config中连接字符串 背景描述 在以前的博客中,我写了许多关于介绍 Asp.net, Gridview, SQL Se ...
- 第六次课:springMVC与spring的集成
spring在项目中起到了管理bean的作用,即可以通过配置,让系统自动创建所需的对象,通过一定的方式引用系统创建的对象,对象的创建和引用都是由spring自动完成的,用户不必参与,可以直接引用. 实 ...
- Problem 2136 取糖果---FUOJ (线段树+维护)
http://acm.fzu.edu.cn/problem.php?pid=2136 题目大意: 给你n个袋子每个袋子里都装有糖果,然后呢你可以每次抽取一个连续的一个区间的袋子,然后带走里面最多糖果数 ...
- Android多点触控技术实战,自由地对图片进行缩放和移动
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11100327 在上一篇文章中我带着大家一起实现了Android瀑布流照片墙的效果, ...
- (Unity)Unity自定义Debug日志文件,利用VS生成Dll文件并使用Dotfuscated进展混淆,避免被反编译
Unity自定义Debug日志文件,利用VS生成Dll文件并使用Dotfuscated进行混淆,避免被反编译. 1.打开VS,博主所用版本是Visual Studio 2013. 2.新建一个VC项目 ...
- List的使用
List<string> AllFilesPath = new List<string>(); ) // get all files path { ; i < files ...
- java里的static和final
本节介绍JAVA里static和final的作用和使用方法以及一些需要注意的问题. 一.static static表示"全局"或"静态",用来修饰成员变量和成员 ...
- stopImmediatePropagation的应用
在众多的方法里面,event.stopImmediatePropagation 算是比较少用的一个方法,拼写上感觉一半像 event.stopPropagation.对于stopPropagation ...