オープニング:毎日の学習における.NET開発は、私たちはしばしば、新しい構文の一部に接触し、彼らは大幅に削減、複雑なコード形式の多くを簡素化するために多くの改善を行って古い文法の前に相対して、比較した当社これらのコードのコード量の初心者農家。しかし、幸せな喜びに離れて、私たちは好奇心私たちのためにことをやっていたものを最終的に内部コンパイラへの立ち入りはできませんか?だから我々は最終的にどのような問題を行うには、コンパイラを見て、逆コンパイルアーティファクトを助けます!多くの人が長時間接触したことのために実際には、このブログのエントリは、新しい構文の多くはありませんが、ここでは主に.NETの古いバージョンのために、それは新しい構文の「相対的」です。
/ *新しい構文インデックス* /
1.自動自動実装プロパティプロパティ2.暗黙の型VAR3.デフォルトのパラメータ値と名前付き引数4.オブジェクト初期化子と初期化子} {設定5. 匿名クラス&匿名メソッド6. 拡張メソッド8. ラムダ式10. LINQクエリ式
自動クエスト属性:[C#3.0 /ネット3.xの新機能]
1.1以前のアプローチ:最初のパブリックプロパティを記述し、プライベート変数を書くこと
パブリッククラス学生 { 民間のInt32 _id。 公共のInt32同上 { {戻り_idを取得します。} 集合{_id =値。} } プライベート文字列_name; 公共の文字列名 { {戻り_nameを取得します。} 集合{_name =値。} } プライベートInt16型の_age。 公共Int16型の年齢 { {戻り_ageを取得します。} 集合{_age =値。} } }
1.2現在の実践:空のプロパティ宣言
パブリッククラスPerson { 公共のInt32 ID {取得します。セットする; } パブリック文字列名前{取得します。セットする; } 公共Int16型年齢{取得します。セットする; } }
PS:それは今では、以下のコードの多くをされていません表示されますか?直接空のプロパティを宣言し、コンパイラは、私たちは前のプライベートメンバフィールドを完了して得るのを助ける方法を設定するので、我々は最終的には、反射板に目を通すためのツールを逆コンパイルすることができますすることができます。この操作を完了する方法です。
1.3グレート "カントリー・スタイル" -CSC(Cシャープコンパイラ):C#コンパイラ
PS:私は中国のファーストフードCSCを食べることを好むので、CSCがそのように何をどのような絶望、第二CSCと呼ばれ、されているのでここではなぜ、まず、農村ベースの言及!(のような農村ベースの広告のように感じるが、私はまだかなりCSCのように、当然のことながら、価格のために確保されています)
(1)まず、我々は上にコンパイルされたexe / dllを、その後、このアプレットをコンパイルするには、トップを見て逆コンパイルアーティファクトリフレクター(またはILSpyも賞賛ダを賞賛)に
CSCを自動的にプライベートフィールドとそれに対応する共通の属性を生成するために、私たちを助けるために:(2)あなたは結果がコンパイルされ見ることができ、Personクラスを見つけます
私たちは、図から見ることができ、自動的に生成されたフィールドは、フィールドの前にいくつかの違いがあります。
①在每个字段上方都加上了一个[CompilerGenerated]的特性(Attribute),顾名思义:表示其是由编译器生成的;
②每个字段的变量名称是有一定格式的,比如<Age>k__BackingField,那么可以看出格式为:<属性名>k_BackingField;(BackingField顾名思义就是背后的字段)
(3)看完了自动生成的字段,再来看看属性是怎么定义的:
①和自动生成的字段一样,属性也加上了[CompilerGenerated]的特性以示区别
②众所周知,属性就是一个get和一个set的两个方法的封装,那么我们之前写的空get/set方法又是怎么被编译生成的呢
于是,我们可以看到,在get和set方法中,也加上了[CompilerGenerated]的特性以示区别,另外还帮我们自动对应了自动生成的私有字段,这就跟我们自己手动写的私有字段+共有属性的方法保持了一致。所以,自动属性是一个实用的语法糖,帮我们做了两件事:自动生成私有字段,自动在get/set方法中匹配私有字段。
二、隐式类型—关键字:var [ C# 3.0/.Net 3.x 新增特性 ]
2.1 犹抱琵琶半遮面—你能猜出我是谁?
以前,我们在定义每个变量时都需要明确指出它是哪个类型。但是,当有了var之后,一切变得那么和谐,我们可以用一个var定义所有的类型。
var age = 100; age += 150; var name = ""; name = "edisonchou"; Console.WriteLine("age={0}", age); Console.WriteLine("name={0}", name);
点击调试,发现编译器自动帮我们匹配上了正确的类型并成功显示出来:
那么,我们又好奇地想知道编译器到底是否识别出来了指定的类型,于是我们再次通过反编译工具来一看究竟:
可以看出,我们可爱的CSC正确地帮我们推断出了正确的类型,不由得想给它点32个赞了!
但是,变量类型不可更改,因为声明的时候已经确定类型了,例如我们在刚刚的代码中给变量赋予不同于定义时的类型,会出现错误。
2.2 好刀用在刀刃上—隐式类型应用场景
在数据型业务开发中,我们会对一个数据集合进行LINQ查询,而这个LINQ查询的结果可能是ObjectQuery<>或IQueryable<>类型的对象。因此,在目标具体类型不明确的情况下,我们可以用var关键来声明:
List<UserInfo> userList = roleService.LoadRoles(param); var data = from u in userList where u.IsDel == 0 select u;
2.3 但“爱”就是克制—隐式类型使用限制
(1)被声明的变量是一个局部变量,而不是静态或实例字段;
(2)变量必须在声明的同时被初始化,编译器要根据初始化值推断类型;
(3)初始化不是一个匿名函数,同时初始化表达式也不能是 null;
(4)语句中只声明一次变量,声明后不能更改类型;(详见上面的例子)
(5)赋值的数据类型必须是可以在编译时确定的类型;
三、参数默认值和命名参数:[ C# 4.0/.NET 4.0 新增特性 ]
3.1 带默认值的方法
static void Main(string[] args) { // 01.带默认值参数函数 FuncWithDefaultPara(); // 02.省略一个默认参数调用 FuncWithDefaultPara(10086); Console.ReadKey(); } static void FuncWithDefaultPara(int id = 10010, bool gender = true) { Console.WriteLine("Id:{0},Gender:{1}", id, gender ? "Man" : "Woman"); }
点击调试,显示结果如下:
3.2 编译后的方法调用
同样,为了一探带参数默认值方法调用的细节,我们还是借助反编译神器查看其中的玄妙:
(1)首先,我们来看看带默认值参数的方法被编译后是怎么的:
可以看到,在.NET Framework中大量采用了基于Attribute的开发方式,这里为参数添加了表示默认值的特性DefaultParameterValue。
(2)其次,再来看看Main函数中的调用过程是怎么被编译的:
可以看出,编译器帮我们在方法调用的括号中帮我们填充了默认值。这里,我们不禁好奇,如果在调用中,不指定ID(即使用ID默认值10010)而仅仅指定Gender为false是否可以编译通过?我们来试一下:
static void Main(string[] args) { // 01.带默认值参数函数 FuncWithDefaultPara(); // 02.省略一个默认参数调用 FuncWithDefaultPara(10086); // 错误调用: FuncWithDefaultPara(false); Console.ReadKey(); }
这时,出现了以下错误:
于是,我们知道,CSC也还没有那么智能,无法理解我们高深的“意图”。那么,有木有一种方法来解决这种需求呢,于是命名参数横空出世了。
3.3 使用命名参数
在新语法中为方法调用引入了命名参数,格式为 参数名:参数值
static void Main(string[] args) { // 01.带默认值参数函数 FuncWithDefaultPara(); // 02.省略一个默认参数调用 FuncWithDefaultPara(10086); // 错误调用: //FuncWithDefaultPara(false); // 03.使用命名参数调用 FuncWithDefaultPara(gender: false); Console.ReadKey(); }
通过调试,可以得到如下结果:
通过前面的分析,我们可以分析出,使用命名参数被编译之后还是会生成指定参数值的调用:
四、自动初始化器:[ C# 3.0/.NET 3.x 新增特性 ]
4.1 属性初始化器
(1)在开发中,我们经常会这些为new出来的对象设置属性:
static void InitialPropertyFunc() { Person p = new Person() { Name = "小强", Age = 18 }; Console.WriteLine("Name:{0}-Age:{1}", p.Name, p.Age); }
(2)但是,经过编译后我们发现原来还是以前的方式:先new出来,然后一个属性一个属性地赋值。这里,编译器首先生成了一个临时对象g_initLocal0,然后为其属性赋值,最后将g_initLocal0这个对象的地址传给要使用的对象p。
4.2 集合初始化器
(1)在开发中,我们经常在一个集合的实例化中,就为其初始化:
static void InitialCollectionFunc() { List<Person> personList = new List<Person>() { new Person(){Name="小强",Age=10}, new Person(){Name="小王",Age=15}, new Person(){Name="小李",Age=18} }; foreach(Person person in personList) { Console.WriteLine("Name:{0}-Age:{1}", person.Name, person.Age); } }
(2)私たちは、コンパイラを学んだ初期化子の上記セットした後、まだそれは、その割り当てられたメモリ空間の後に最初の新しいもので、その後、自分の財産の譲渡に一つずつ、元の方法にコンパイルされます。その後、我々は推測をハザードことができ、コレクションの初期化中に、コンパイラは、より多くの作業を行うために最適化されています。最初のうち新しい各オブジェクト、およびそのプロパティの割り当てのための一つ一つを、最後の呼び出しのコレクションが追加に追加する方法をコレクションへ。だから、我々はそれを逆コンパイル、それをチェックアウト:
ダウンロードの添付ファイル
(1)デコンパイルアーティファクトリフレクター: http://pan.baidu.com/s/1evCJG
(2)いわゆる新しい構文デモ:http://pan.baidu.com/s/1ntwqdAT
著者:周Xulong
出典:http://www.cnblogs.com/edisonchou/
この記事では、著者とブログパークの合計に属し転載を歓迎しますが、この節で宣言され、著者の同意なしに保持され、見かけ上の位置にある元の記事のページへのリンクを指定する必要があります。