Lambda Expressions

无论是expression lambda还是statement lambda,都适用以下的描述:

Lambda Expressions – This is a shorthand for specifying a method. The C# compiler will translate each into either an anonymous method or a true System.Linq.Expressions.Expression. You really need to understand these to use Linq well. There are three parts: A parameter list, an arrow, and a method body.

 

1.匿名方法的例子:(匿名方法可以作为delegate直接传给接受Action<>,Func<>这些concrete delegate的地方,也就是编译器可以自动转换,但如果接受方需要的是Delegate这个抽象的类对象,比如Dispatch.Invoke方法,就要类型转换,这里有讨论这个问题的。)

new Action(() =>{…})

lambda表达式变成了 .method public hidebysig instance void <LoadData>b__8() cil managed

    <>c__DisplayClassa CS$<>8__localsb; (这里Reflector翻译的C#不准,看IL指令集,实际是newobj指令,把当前context创建出来)
      CS$<>9__CachedAnonymousMethodDelegate9 = new Action(CS$<>8__localsb, (IntPtr) this.<LoadData>b__8);
(调用了Lambda表达式形成的匿名方法)

 

2.转成Expressions的例子,生成了暴多的IL,应用了很多Expressons命名空间的类和方法:

h => h.CompanyCode == "1"  会变成 Expression<Func<T, bool>> expr

: ldtoken [CdcSoftware.Erp.RuntimeServices]CdcSoftware.Erp.RuntimeServices.Entities.Finance.SalesOrderHeader
    L_0012: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    L_0017: ldstr "h"
    L_001c: call class [System.Core]System.Linq.Expressions.ParameterExpression [System.Core]System.Linq.Expressions.Expression::Parameter(class [mscorlib]System.Type, string)
    L_0021: stloc.3
    L_0022: ldloc.3
    L_0023: ldtoken instance string [CdcSoftware.Erp.RuntimeServices]CdcSoftware.Erp.RuntimeServices.Entities.Finance.SalesOrderHeader::get_CompanyCode()
    L_0028: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
    L_002d: castclass [mscorlib]System.Reflection.MethodInfo
    L_0032: call class [System.Core]System.Linq.Expressions.MemberExpression [System.Core]System.Linq.Expressions.Expression::Property(class [System.Core]System.Linq.Expressions.Expression, class [mscorlib]System.Reflection.MethodInfo)
    L_0037: ldstr "1"
    L_003c: ldtoken string
    L_0041: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    L_0046: call class [System.Core]System.Linq.Expressions.ConstantExpression [System.Core]System.Linq.Expressions.Expression::Constant(object, class [mscorlib]System.Type)
    L_004b: ldc.i4.0
    L_004c: ldtoken bool [mscorlib]System.String::op_Equality(string, string)
    L_0051: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
    L_0056: castclass [mscorlib]System.Reflection.MethodInfo
    L_005b: call class [System.Core]System.Linq.Expressions.BinaryExpression [System.Core]System.Linq.Expressions.Expression::Equal(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.Expression, bool, class [mscorlib]System.Reflection.MethodInfo) //这时栈内是第一个参数,lambda的body Expression
    L_0060: ldc.i4.1
    L_0061: newarr [System.Core]System.Linq.Expressions.ParameterExpression
    L_0066: stloc.s CS$0$0002
    L_0068: ldloc.s CS$0$0002
    L_006a: ldc.i4.0
    L_006b: ldloc.3
    L_006c: stelem.ref
    L_006d: ldloc.s CS$0$0002 //把ParameterExpression[0]加载到栈里(第二个参数,lambda的左边的参数集合)
    L_006f: call class [System.Core]System.Linq.Expressions.Expression`1<!!0> [System.Core]System.Linq.Expressions.Expression::Lambda<class [mscorlib]System.Func`2<class [CdcSoftware.Erp.RuntimeServices]CdcSoftware.Erp.RuntimeServices.Entities.Finance.SalesOrderHeader, bool>>(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.ParameterExpression[])

题外话:关于IL的参数顺序,生成的IL都是从左到右压到栈里的,但为什么网上都说c#是stdcall呢(也是从右到左的)。这里指出:“当我们使用 call 等指令调用一个方法时,CLR 为目标方法在调用堆栈上新分配一个堆栈帧,并将方法参数从当前方法的计算堆栈弹出压入目标方法的参数表中,接着执行流程跳转到目标方法。”注意弹出压入一词,在老方法里压栈的顺序是param1,param2,则老栈内是param2在param1上面 弹出压入新参数表栈后,由于是param2先弹压,就变成了param2在param1下面了。这就是从右到左的过程。

连接到VPN后访问本地网络的方法

来源:这里

方法一:配置VPN。

连接上VPN之后,查看VPN的属性,然后依次点击:

网络->Internet协议(Tcp/IP)->属性->高级->常规,去除“在远程网络上使用默认网关”前面的勾。

这种方法可以保证远程内网和本地内网的同时方法,但是,如果远程网络中有多个子网,就需要在VPN服务器端进行设置了。有一定的局限性。

方法二:设置本地的路由。

使得路由可以保证本地网络地址使用本地的网管进行跳转。具体方法如下:

1、在开始菜单的“运行”中输入cmd

2、输入ipconfig /all这个命令。得到的结果可能如下:

C:\Documents and Settings\Administrator>ipconfig /all

Windows IP Configuration Host Name . . . . . . . . . . . . : MyName Primary Dns Suffix . . . . . . . : Node Type . . . . . . . . . . . . : Mixed IP Routing Enabled. . . . . . . . : No

WINS Proxy Enabled. . . . . . . . : No

Ethernet adapter 本地连接: Connection-specific

DNS Suffix . :

Description . . . . . . . . . . . : Broadcom 440x 10/100 Integrated Controller

Physical Address. . . . . . . . . : 00-1A-A0-B1-C2-B8

Dhcp Enabled. . . . . . . . . . . : No

IP Address. . . . . . . . . . . . : 192.168.0.86

Subnet Mask . . . . . . . . . . . : 255.255.0.0

Default Gateway . . . . . . . . . : 192.168.0.1

DNS Servers . . . . . . . . . . . : 192.168.0.1

PPP adapter VPN: Connection-specific

DNS Suffix . :

Description . . . . . . . . . . . :

WAN (PPP/SLIP) Interface Physical

Address. . . . . . . . . : 00-53-45-00-00-00

Dhcp Enabled. . . . . . . . . . . : No

IP Address. . . . . . . . . . . . : 14X.3X.6X.110

Subnet Mask . . . . . . . . . . . : 255.255.255.255

Default Gateway . . . . . . . . . : 14X.3X.6X.110

DNS Servers . . . . . . . . . . . : 20X.10X.3X.141

其中本地连接的默认网关是192.168.0.1。现在我们需要将192.168.0.0这个子网段的所有访问通过这个网管来跳转,就可以在cmd这个命令下面继续输入:
route -p add 192.168.0.0 mask 192.168.0.0 192.168.0.1
这样就可以保证这个网段的内网地址仅通过默认的网管进行访问,而不经过VPN了。

因为路由设置重启之后就失效了,所以最好建立一个批处理文件.bat然后将上面新增路由的命令放到里面,添加到开始菜单的启动中,每次开机启动后执行。这样就可以保证VPN连接后仍然可以访问本地的网络了。