1. 隐含类型局部变量
var关键字,告诉编译器(对于CLR来说,它是不会知道你是否使用了var, 编译器将其编译为实际类型)自己去推断它的类型。既然让编译器推断类型就必须声明的时候赋值,而且不能是null值。注意,这只能用于局部变量,用于字段是不可以的。
code:
private void TestVarDeclare()
{
var age = 26;
var userName = "Archer";
var list = new[] { "Archer", "LeeWei", "Alona" };
foreach (var user in list)
{
Console.WriteLine(user);
}
// Console.Read();
}
IL:
.locals init ([0] int32 age,
[1] string userName,
[2] string[] list,
[3] string user,
[4] string[] CS$0$0000,
[5] string[] CS$6$0001,
[6] int32 CS$7$0002,
[7] bool CS$4$0003)
从IL可以看出,编译器已经将其编译为合适的数据类型。
2. 匿名类型
匿名类型允许开发人员定义行内类型,无须显式定义类型。常和var配合使用,var用于声明匿名类型。
private void AnonymityClassTest()
{
var data = new {username = "Archer", age = 20};
Console.WriteLine(data.username);
Console.WriteLine(data.age);
}
IL:
.method private hidebysig instance void AnonymityClassTest() cil managed
{
// Code size 39 (0x27)
.maxstack 3
.locals init ([0] class '<>f__AnonymousType0`2'<string,int32> data)
IL_0000: nop
IL_0001: ldstr "Archer"
IL_0006: ldc.i4.s 20
IL_0008: newobj instance void class '<>f__AnonymousType0`2'<string,int32>::.ctor(!0,
!1)
IL_000d: stloc.0
IL_000e: ldloc.0
IL_000f: callvirt instance !0 class '<>f__AnonymousType0`2'<string,int32>::get_username()
IL_0014: call void [mscorlib]System.Console::WriteLine(string)
IL_0019: nop
IL_001a: ldloc.0
IL_001b: callvirt instance !1 class '<>f__AnonymousType0`2'<string,int32>::get_age()
IL_0020: call void [mscorlib]System.Console::WriteLine(int32)
IL_0025: nop
IL_0026: ret
} // end of method Program::AnonymityClassTest
另外,我们可以在IS Disassembler中看见这个<>f__AnonymousType0`2'是作为一个单独的类存在的
3. 扩展方法
扩展方法的定义,需要注意三个部分:1、静态类(私有公共都可以);2、静态方法(私有公共都可以);3、第一个函数参数前带 this(必须是第一参数前)
,如果扩展方法名和原有方法名发生冲突,那么扩展方法将失效。
Code:
public static class ExtensionMethodClass
{
public static string AddString(this string str, string input)
{
return str + input;
}
}
private void ExtensionMethod()
{
string testStr = "Archer";
string result = testStr.AddString("Chen");
Console.WriteLine(result);
}
IL:
扩展方法实际是编译器调用某个类的某个方法的时候,先去这个类找,如果有这个方法,则就调用;如果找不到,根据引用的命名空间,再去找扩展方法(静态类的静态方法)。找到,就使用,找不到当然就编译错误了。
由此看来扩展方法和隐藏变量一样,也是编译器干的苦活, 而跟CLR无关
4. 自动属性
这个没有什么意思, 纯粹是个偷懒的方法。编译器自动为你生成get、set操作以及字段,并且你不能使用私有字段(由编译器自动生成)也不能自定义get、set操作,不过你可以分别定义get和set的访问级别。
Code:
public class Person
{
public string username { get; protected set; }
public int age { get; set; }
public Person()
{
this.username = "zhuye";
}
}
IL:
会发现中间语言和自己写区别不大, 只是自动添加了<username>k__BackingField, 和<age>k__BackingField两个私有字段。
5. 对象初始化器
编译器会自动为你做setter操作,使得原本几行的属性赋值操作能在一行中完成。这里需要注意:
1. 允许只给一部分属性赋值,包括internal访问级别
2. 可以结合构造函数一起使用,并且构造函数初始化先于对象初始化器执行
Code:
Person p = new Person() {username = "Archer", age=26};
Console.WriteLine(p.ToString());
IL:
IL_0001: newobj instance void CSharp3._Test.Person::.ctor()
IL_0006: stloc.1
IL_0007: ldloc.1
IL_0008: ldstr "archer"
IL_000d: callvirt instance void CSharp3._Test.Person::set_username(string)
IL_0012: nop
IL_0013: ldloc.1
IL_0014: ldc.i4.s 26
IL_0016: callvirt instance void CSharp3._Test.Person::set_age(int32)
从IL中可以看见, 编译器现调用构造函数构造对象,然后调用对象的属性赋值。
注Person p = new Person() {username = "Archer", age=26};和new person {username = "Archer", age=26}, 参数为空,和不指定参数是有区别的
6.集合初始化器
和对象初始化器类同
7. Lambda表达式
其实和2.0中的匿名方法差不多,都是用于产生内联方法,只不过Lambda表达式的语法更为简洁。语法如下:
(参数列表) => 表达式或者语句块
其中:
参数个数:可以有多个参数,一个参数,或者无参数。
表达式或者语句块:这部分就是我们平常写函数的实现部分(函数体)。
Code:
var list = new [] { "aa", "bb", "ac" };
var result = Array.FindAll(list, s => (s.IndexOf("a") > -1));
foreach (var v in result)
{
Console.WriteLine(v);
}
IL:
.locals init ([0] bool CS$1$0000)
IL_0000: ldarg.0
IL_0001: ldstr "a"
IL_0006: callvirt instance int32 [mscorlib]System.String::IndexOf(string)
IL_000b: ldc.i4.m1
IL_000c: cgt
IL_000e: stloc.0
IL_000f: br.s IL_0011
IL_0011: ldloc.0
IL_0012: ret
8. 查询句法
查询句法是使用标准的LINQ查询运算符来表达查询时一个方便的声明式简化写法。该句法能在代码里表达查询时增进可读性和简洁性,读起来容易,也容易让人写对。Visual Studio 对查询句法提供了完整的智能感应和编译时检查支持。编译器在底层把查询句法的表达式翻译成明确的方法调用代码,代码通过新的扩展方法和Lambda表达式语言特性来实现。