.NET是一门多语言平台,这是我们所众所周知的,其实现原理在于因为了MSIL(微软中间语言)的一种代码指令平台。所以.NET语言的编译就分为了两 部分,从语言到MSIL的编译(我喜欢成为预编译),和运行时的从MSIL到本地指令,即时编译(JIT)。JIT编译分为经济编译器和普通编译器,在这 里就不多说了,不是本文的重点。本文主要讨论下预编译过程中我们能做的改变编译情况,改变生成的IL,从编译前后看看微软C#3.0一些语法 糖,PostSharp的静态注入等等。
1:我们先来看看最简单的var:
C#:
public
void
TestVar()
{
var i
=
0
;
Console.WriteLine(i);
}
使用Reflector查看生成
IL:
反编译后的C#:
这里VS在编译的时候将var为我们转变为了int类型。
2:Action<int>:
C#:
public
void
TestAction()
{
var i
=
1
;
Func
<
int
,
int
>
f
=
t
=>
t
+
1
;
i
=
10
;
f(i);
}
反编译后C#:
编译器为我们在这里生成了代理方法。
总结:
关于lambda表达式的编译规则:
当一个lambda expression被赋值给一个delegate类型,例如Action<T>或者Func<T, TResult>等,这个lambda expression会被编译器直接编译为
1) 当lambda expression没有使用闭包内的非局部引用也没有使用到this时,编译为一个私有静态方法;
2) 当lambda expression没有使用闭包内的非局部引用,但用到了this时,编译为一个私有成员方法;
3) 当lambda expression中引用到非局部变量,则编译为一个私有的内部类,将引用到的非局部变量提升为内部类的。
3:PostSharp:
PostSharp是结合了 MSBuild Task 和 MSIL Injection 技术,编译时静态注入实现 AOP 编程。在编译时候改变VS的编译行为。更详细的信息,请访问 PostSharp 网站
原c#:
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
namespace
ConsoleApplication1
{
class
Program
{
static
void
Main(
string
[] args)
{
new
Program().TestPostSharp();
}
[ErrorHandler()]
public
void
TestPostSharp()
{
throw
new
Exception(
"
I will throw a exception!
"
);
}
}
[Serializable]
public
class
ErrorHandlerAttribute : PostSharp.Laos.OnMethodBoundaryAspect
{
public
override
void
OnException(PostSharp.Laos.MethodExecutionEventArgs eventArgs)
{
//
do some AOP operation!
Console.WriteLine(eventArgs.Method
+
"
:
"
+
eventArgs.Exception.Message);
eventArgs.FlowBehavior
=
PostSharp.Laos.FlowBehavior.Continue;
}
}
}
反编译后:
今天就到此为至,只是简单的了解下IL注入实例,在后面会利用MSBuild Task+Mono Cecil 和PostSharp实现一些简单的注入实例.
在我们的代码预编译过程中我们可以创建我们自己的任务Task。下面我们就开始做一个简单的Task。
1:首先需要添加Microsoft.Build.Utilities.v3.5.dll和 Microsoft.Build.Framework.dll中引用。在Microsoft.Build.Framework中为我们定义了接口 ITask,自定义任务需要去实现这个契约。其定义如下:
public
interface
ITask
{
IBuildEngine BuildEngine {
get
;
set
; }
ITaskHost HostObject {
get
;
set
; }
bool
Execute();
}
IBuildEngine 从字义上说是编译引擎,他主要承载了我们的编译生成时的信息和消息,警告,错误等事件注册。而Execute这是我们Task执行体。HostObject 任务关联宿主信息。
在Microsoft.Build.Utilities下Task为我们实现了基本的ITask信息。我们可以从这里继承开始。
下面是一个简单的实现:
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
namespace
FirstBuildTask
{
public
class
MyBuildTask : Microsoft.Build.Utilities.Task
{
private
string
outputFile;
[Microsoft.Build.Framework.Required]
public
string
OutputFile
{
get
{
return
outputFile; }
set
{ outputFile
=
value; }
}
public
override
bool
Execute()
{
Log.LogWarning(
"
test message:
"
+
this
.outputFile);
return
true
;
}
}
}
下面我们需要关联Task,本人不喜欢污染,所以加到csproject(也可全局所有项目使用)。
下面我们创建一个简单的控制台
程序
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
namespace
BlogSample
{
class
Program
{
static
void
Main(
string
[] args)
{
Console.WriteLine(
"
ok
"
);
Console.Read();
}
}
}
用记事本形式打开csproject文件:
添加我们的Task声明和任务。(xml标记含义将在后续,这里先看看效果。)
声明Task:
<
UsingTask
TaskName
="MyBuildTask"
AssemblyFile
="XXXX\bin\Debug\FirstBuildTask1.dll"
/>
添加任务:
<
Target
Name
="AfterBuild"
>
<
MyBuildTask
OutputFile
="$(MSBuildProjectFullPath)"
/>
</
Target
>
修改后的csproject文件形如:
View Code
<?
xml version="1.0" encoding="utf-8"
?>
<
Project
ToolsVersion
="3.5"
DefaultTargets
="Build"
xmlns
="http://schemas.microsoft.com/developer/msbuild/2003%22>
<PropertyGroup>
<Configuration Condition="
'$(Configuration)'
==
'' "
>
Debug
</
Configuration
>
<
Platform
Condition
=" '$(Platform)' == '' "
>
AnyCPU
</
Platform
>
<
ProductVersion
>
9.0.30729
</
ProductVersion
>
<
SchemaVersion
>
2.0
</
SchemaVersion
>
<
ProjectGuid
>
{844D0C87-9808-4AE9-8906-0382D9DDF88A}
</
ProjectGuid
>
<
OutputType
>
Exe
</
OutputType
>
<
AppDesignerFolder
>
Properties
</
AppDesignerFolder
>
<
RootNamespace
>
BlogSample
</
RootNamespace
>
<
AssemblyName
>
BlogSample
</
AssemblyName
>
<
TargetFrameworkVersion
>
v3.5
</
TargetFrameworkVersion
>
<
FileAlignment
>
512
</
FileAlignment
>
</
PropertyGroup
>
<
PropertyGroup
Condition
=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "
>
<
DebugSymbols
>
true
</
DebugSymbols
>
<
DebugType
>
full
</
DebugType
>
<
Optimize
>
false
</
Optimize
>
<
OutputPath
>
bin\Debug\
</
OutputPath
>
<
DefineConstants
>
DEBUG;TRACE
</
DefineConstants
>
<
ErrorReport
>
prompt
</
ErrorReport
>
<
WarningLevel
>
4
</
WarningLevel
>
</
PropertyGroup
>
<
PropertyGroup
Condition
=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "
>
<
DebugType
>
pdbonly
</
DebugType
>
<
Optimize
>
true
</
Optimize
>
<
OutputPath
>
bin\Release\
</
OutputPath
>
<
DefineConstants
>
TRACE
</
DefineConstants
>
<
ErrorReport
>
prompt
</
ErrorReport
>
<
WarningLevel
>
4
</
WarningLevel
>
</
PropertyGroup
>
<
ItemGroup
>
<
Reference
Include
="System"
/>
<
Reference
Include
="System.Core"
>
<
RequiredTargetFramework
>
3.5
</
RequiredTargetFramework
>
</
Reference
>
<
Reference
Include
="System.Xml.Linq"
>
<
RequiredTargetFramework
>
3.5
</
RequiredTargetFramework
>
</
Reference
>
<
Reference
Include
="System.Data.DataSetExtensions"
>
<
RequiredTargetFramework
>
3.5
</
RequiredTargetFramework
>
</
Reference
>
<
Reference
Include
="System.Data"
/>
<
Reference
Include
="System.Xml"
/>
</
ItemGroup
>
<
ItemGroup
>
<
Compile
Include
="DisposeSubstance.cs"
/>
<
Compile
Include
="Program.cs"
/>
<
Compile
Include
="Properties\AssemblyInfo.cs"
/>
</
ItemGroup
>
<
Import
Project
="$(MSBuildToolsPath)\Microsoft.CSharp.targets"
/>
<
UsingTask
TaskName
="MyBuildTask"
AssemblyFile
="XXXXX\bin\Debug\FirstBuildTask1.dll"
/>
<
Target
Name
="BeforeBuild"
>
</
Target
>
<
Target
Name
="AfterBuild"
>
<
MyBuildTask
OutputFile
="$(MSBuildProjectFullPath)"
/>
</
Target
>
</
Project
>
此时我们可以用VS重新加载编译或者是MSBuild控制台编译:
效果如下:
上边红线注释的就是我们的Task所做的警告和输出路径的提示。
我们已经完成了一个简单Task,但还遗留这MsBuild配置,下面将简单的描述。
1:UsingTask:定义:<UsingTask TaskName="TaskName" AssemblyName = "AssemblyName" TaskFactory = "ClassName" Condition="'String A'=='String B'" />
描述我们的任务的程序集,任务类等信息,具体参见http://msdn.microsoft.com/zh-cn/library/t41tzex2.aspx。
属性 |
说明 |
AssemblyName |
AssemblyName 或 AssemblyFile 属性是必需的。 要加载的程序集的名称。尽管 AssemblyName 属性不是必需的,但它接受强名称程序集。使用此属性等效于通过 .NET Framework 中的 Load 方法加载程序集。 如果使用了 AssemblyFile 属性,便不能使用此属性。 |
AssemblyFile |
AssemblyName 或 AssemblyFile 属性是必需的。 程序集的文件路径。此属性既接受完整路径,也接受相对路径。相对路径是相对于当前项目目录的路径。使用此属性等效于通过 .NET Framework 中的 LoadFrom 方法加载程序集。 如果使用了 AssemblyName 属性,便不能使用此属性。 |
TaskName |
必选的属性。 要从程序集中引用的任务的名称。如果可能存在多义性,则此属性应该始终指定完整的命名空间。如果存在多义性,MSBuild 将选择任意匹配方式,该匹配方式可能产生意外的结果。 |
Condition |
可选的属性。 要计算的条件。有关更多信息,请参见 MSBuild 条件。 |
在系统中为我们定义了很多Task,有:
-
AL(程序集链接器)任务:描述
AL 任务及其参数。
-
AspNetCompiler 任务:包装 aspnet_compiler.exe,它是预编译 ASP.NET 应用程序的实用工具。
-
AssignCulture 任务:为项分配区域性标识符。
-
CallTarget 任务:调用项目文件中的目标。
-
Copy 任务:描述
Copy 任务及其参数。
-
CreateItem 任务:描述
CreateItem 任务及其参数。
-
CreateProperty 任务:描述
CreateProperty 任务及其参数。
-
Csc 任务:描述
Csc 任务及其参数。
-
Delete 任务:描述
Delete 任务及其参数。
-
Error 元素 (MSBuild):根据计算的条件语句停止生成操作并记录错误。
-
Exec 任务:描述
Exec 任务及其参数。
-
FindUnderPath 任务:确定指定项集合中的哪些项存在于指定的文件夹及其所有子文件夹中。
-
GenerateApplicationManifest 任务:描述
GenerateApplicationManifest 任务及其参数。
-
GenerateBootstrapper 任务:提供一种自动化方式来检测、下载和安装应用程序及其必备组件。
-
GenerateDeploymentManifest 任务:描述
GenerateDeployManifest 任务及其参数。
-
GenerateResource 任务:将 .txt 和 .resx 文件转换为公共语言运行库二进制 .resources 文件。
-
GetAssemblyIdentity 任务:从指定的文件检索程序集标识并输出标识信息。
-
GetFrameworkPath 任务:检索 .NET Framework 程序集的路径。
-
GetFrameworkSdkPath 任务:检索 .NET Framework SDK 的路径。
-
LC 任务:描述
LC 任务及其参数。
-
MakeDir 任务:描述
MakeDir 任务及其参数。
-
Message 元素 (MSBuild):在生成期间记录消息。
-
MSBuild 任务:描述 MSBuild 任务及其参数。
-
ReadLinesFromFile 任务:从文本文件读取项列表。
-
RegisterAssembly 任务:描述
RegisterAssembly 任务及其参数。
-
RemoveDir 任务:描述
RemoveDir 任务及其参数。
-
ResGen 任务:描述
ResGen 任务及其参数。
-
ResolveAssemblyReference 任务:描述
ResolveAssemblyReference 任务及其参数。
-
ResolveComReference 任务:描述
ResolveCOMReference 任务及其参数。
-
ResolveKeySource 任务:确定强名称密钥源
-
ResolveNativeReference 任务:解析本机引用。
-
SGen 任务:为指定程序集中的类型创建一个 XML 序列化程序集。
-
SignFile 任务:使用指定证书对指定文件进行签名。
-
Touch 任务:描述
Touch 任务及其参数。
-
UnregisterAssembly 任务:描述
UnregisterAssembly 任务及其参数。
-
Vbc 任务”描述
Vbc 任务及其参数。
-
VCBuild 任务“描述
VCBuild 任务及其参数。
-
Warning 元素 (MSBuild):根据计算的条件语句在生成期间记录警告。
-
WriteLinesToFile 任务:将指定项写入指定的文本文件。
-
其实很多我都不知道,我经常是用的时候在去查MSDN:
http://msdn.microsoft.com/zh-cn/library/7z253716(v=VS.80).aspx
2:Target:定义
<
Target
Name
="Target Name"
DependsOnTargets
="DependentTarget"
Inputs
="Inputs"
Outputs
="Outputs"
Condition
="'String A' == 'String B'"
>
<
Task
>
...
</
Task
>
<
OnError...
/>
</
Target
>
描述了我们的目标:可以包含0个多个任务。
属性
属性 |
说明 |
Name |
必选的属性。 目标的名称。 |
DependsOnTargets |
可选的属性。 在执行此目标或者进行顶级依赖项分析之前必须执行的目标。多个目标之间用分号分隔。 |
Inputs |
可选的属性。 此目标的项输入。此属性中的项用作顶级依赖项分析中的输入。 |
Outputs |
可选的属性。 此目标的预期输出。可以通过对输入项应用转换来生成输出项。有关转换的更多信息,请参见 MSBuild 转换。 |
Condition |
可选的属性。 要计算的条件。如果条件的计算结果为 false,目标将不会执行该目标的体或者在 DependsOnTargets 属性中设置的任何目标的体。有关条件的更多信息,请参见 MSBuild |
msdn:http://msdn.microsoft.com/zh-cn/library/t50z2hka(VS.80).aspx
参考文献:
http://msdn.microsoft.com/zh-cn/library/t41tzex2.aspx
http://msdn.microsoft.com/zh-cn/library/t50z2hka(VS.80).aspx
http://msdn.microsoft.com/zh-cn/library/ms171466(v=VS.80).aspx
http://msdn.microsoft.com/zh-cn/library/7z253716(v=VS.80).aspx
http://msdn.microsoft.com/zh-cn/library/ms164313(v=VS.80).aspx
http://msdn.microsoft.com/zh-cn/library/t9883dzc(v=VS.80).aspx
http://msdn.microsoft.com/zh-cn/library/ms171464(v=VS.80).aspx
MSBuild概念:http://msdn.microsoft.com/zh-cn/library/ms171451(v=VS.80).aspx