博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#4.0初探:dynamic 关键字
阅读量:2436 次
发布时间:2019-05-10

本文共 9819 字,大约阅读时间需要 32 分钟。

C#新增了dynamic关键字,正因为这一个小小的关键字,C#动态特性向前迈进了一大步。

dynamic是一个类型关键字,声明为dynamic的类型与"静态类型"(这里的静态类型是指编译时确定的类型,下同)相比最大的特点它是"动态类型",它会运行时尝试调用方法,这些方法的存在与否不是在编译时检查的,而是在运行时查找,如果方法存在并且参数正确,会正常调用,否则会抛出Microsoft.CSharp.RuntimeBinder.RuntimeBinderException异常。


看一个最简单的示例:

using System; 
namespace  .Net.TestDynamic 
    
class Program 
    { 
        
static 
void Main() 
        { 
            dynamic d = Console.Out; 
            dynamic a; 
            a = 
new Int32(); 
            
int b = a; 
            a++; 
            a--; 
            d.WriteLine(
"
http://www.xianfen.net/
"); 
            d.WriteLine(d.GetType()); 
            d.writeln(
"
test
"); 
//
抛出Microsoft.CSharp.RuntimeBinder.RuntimeBinderException异常
 
        } 
    } 
}

对dynamic类型的操作只能有以下几种:

·赋值

·方法调用

·自增

·自减

·接受"静态类型"的构造器创建的对象


与关键字var的比较

从表面上看,dynamic与var关键字的用法很像,但实质上有本质区别。

var关键字被称为:隐含类型局部变量(Local Variable Type Inference),var只能用作局部变量,不能用于字段、参数等;声明的同时必须初始化;初始化时类型就已经明确了,并且不能再被赋值不能进行隐式类型转换的类型的数据;编译时编译器会对var定义的变量进行类型推断,这时变量的类型已经被确定。

dynamic可用于类型的字段,方法参数,方法返回值,可用于泛型的类型参数等;可以赋值给或被赋值任何类型并且不需显式的强制类型转换,因为这些是运行时执行的,这要得益于dynamic类型的动态特性。


与反射的比较

首先能看到的是,dynamic与反射相比,执行相同操作所需的代码少的多。

如调用类Me中的GetName()方法。

class Me 
    
public 
string Blog { 
get
set; } 
    
public 
string GetName() 
    { 
        
return 
"
Zhenxing Zhou
"
    } 
}

用反射调用GetName()方法:

Assembly a = Assembly.GetExecutingAssembly(); 
object instance = a.CreateInstance(
"
Xianfen.Net.TestDynamic.Me
"); 
Type type = instance.GetType(); 
MethodInfo mi = type.GetMethod(
"
GetName
"); 
object result = mi.Invoke(instance, 
null);

同样的dynamic调用:

dynamic myInfo = 
new Me(); 
string result = myInfo.GetName();

dynamic类型与反射相比能进行的操作要少的多。

目前dynamic类型对属性调用是不可用的,但我们知道,属性生成IL时,对属性的读或写会生成对应的在属性名前加上get_或set_前缀生成相应的方法,尝试调用两个方法来访问属性:

dynamic myInfo = 
new Me(); 
myInfo.set_Blog(
"
http://www.xianfen.net/
"); 
string result = myInfo.get_Blog();

会抛出异常,提示找不到get/set_Blog方法。这点比较遗憾,同样,对有参属性的访问也是不行的。

反射还可以访问私有方法字段以及其它类型成员及取得类型及类型成员的信息等。


dynamic类型的效率

效率问题应该是大家很关心的,我的感觉:不要对动态语言有很高的效率抱有太大的希望,但另一方面,算法的设计对效率的影响非常大,功能与性能经常存在一个平衡点。

要分析其效率,就要看看编译后内部都干了些啥,方法是写些简单的代码,查看IL。

代码:

using System; 
namespace  .Net.TestDynamic 
    
class Program 
    { 
        
static 
void Main() 
        { 
            dynamic d = 
"
str
"
            d.ToString(); 
        } 
    } 
}

对应的IL代码:

.class 
private 
auto 
ansi 
beforefieldinit Program 
    
extends [mscorlib]System.Object 
    
.method 
public 
hidebysig 
specialname 
rtspecialname 
instance 
void .ctor() 
cil 
managed 
    { 
        
.maxstack 
8 
        
L_0000: 
ldarg.0 
        
L_0001: 
call 
instance 
void [mscorlib]System.Object::.ctor() 
        
L_0006: 
ret 
    } 
    
.method 
private 
hidebysig static 
void Main() 
cil 
managed 
    { 
        
.entrypoint 
        
.maxstack 
9 
        
.locals 
init ( 
            [
0
object d, 
            [
1
class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo[] CS$
0$
0000
        
L_0000: 
ldstr 
"
str
" 
        
L_0005: 
stloc.0 
        
L_0006: 
ldsfld 
class [System.Core]System.Runtime.CompilerServices.CallSite`
1<
class [mscorlib]System.Action`
2<
class[System.Core]System.Runtime.CompilerServices.CallSite, 
object>> Xianfen.Net.TestDynamic.Program/<Main>o__SiteContainer0::<>p__Site1 
        
L_000b: 
brtrue.s L_003f 
        
L_000d: 
ldc.i4.0 
        
L_000e: 
ldstr 
"
ToString
" 
        
L_0013: 
ldtoken Xianfen.Net.TestDynamic.Program 
        
L_0018: 
call 
class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) 
        
L_001d: 
ldnull 
        
L_001e: 
ldc.i4.1 
        
L_001f: 
newarr [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo 
        
L_0024: 
stloc.1 
        
L_0025: 
ldloc.1 
        
L_0026: 
ldc.i4.0 
        
L_0027: 
ldc.i4.0 
        
L_0028: 
ldnull 
        
L_0029: 
newobj 
instance 
void [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::.ctor(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, 
string
        
L_002e: 
stelem.ref 
        
L_002f: 
ldloc.1 
        
L_0030: 
newobj 
instance 
void [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder::.ctor(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpCallFlags, 
string
class [mscorlib]System.Type, 
class[mscorlib]System.Collections.Generic.IEnumerable`
1<
class [mscorlib]System.Type>, 
class [mscorlib]System.Collections.Generic.IEnumerable`
1<
class[Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>) 
        
L_0035: 
call 
class [System.Core]System.Runtime.CompilerServices.CallSite`
1<!
0> [System.Core]System.Runtime.CompilerServices.CallSite`
1<
class[mscorlib]System.Action`
2<
class [System.Core]System.Runtime.CompilerServices.CallSite, 
object>>::Create(
class[System.Core]System.Runtime.CompilerServices.CallSiteBinder) 
        
L_003a: 
stsfld 
class [System.Core]System.Runtime.CompilerServices.CallSite`
1<
class [mscorlib]System.Action`
2<
class[System.Core]System.Runtime.CompilerServices.CallSite, 
object>> Xianfen.Net.TestDynamic.Program/<Main>o__SiteContainer0::<>p__Site1 
        
L_003f: 
ldsfld 
class [System.Core]System.Runtime.CompilerServices.CallSite`
1<
class [mscorlib]System.Action`
2<
class[System.Core]System.Runtime.CompilerServices.CallSite, 
object>> Xianfen.Net.TestDynamic.Program/<Main>o__SiteContainer0::<>p__Site1 
        
L_0044: 
ldfld !
0 [System.Core]System.Runtime.CompilerServices.CallSite`
1<
class [mscorlib]System.Action`
2<
class[System.Core]System.Runtime.CompilerServices.CallSite, 
object>>::Target 
        
L_0049: 
ldsfld 
class [System.Core]System.Runtime.CompilerServices.CallSite`
1<
class [mscorlib]System.Action`
2<
class[System.Core]System.Runtime.CompilerServices.CallSite, 
object>> Xianfen.Net.TestDynamic.Program/<Main>o__SiteContainer0::<>p__Site1 
        
L_004e: 
ldloc.0 
        
L_004f: 
callvirt 
instance 
void [mscorlib]System.Action`
2<
class [System.Core]System.Runtime.CompilerServices.CallSite, 
object>::Invoke(!
0, !
1
        
L_0054: 
ret 
    } 
    
.class abstract 
auto 
ansi sealed 
nested 
private 
beforefieldinit <Main>o__SiteContainer0 
        
extends [mscorlib]System.Object 
    { 
        
.custom 
instance 
void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() 
        
.field 
public static 
class [System.Core]System.Runtime.CompilerServices.CallSite`
1<
class [mscorlib]System.Action`
2<
class[System.Core]System.Runtime.CompilerServices.CallSite, 
object>> <>p__Site1 
    } 
}

可以看出生成的IL代码确实不美观,不过大体能看出端倪。为了方便查看,用Reflector查看,把反编译结果设置为.net2.0,代码清晰多了:

01
internal 
class Program 
02. { 
03.     
//
 Methods
 
04.     
private 
static 
void Main() 
05.     { 
06.         
object d = 
"
str
"
07.         
if (<Main>o__SiteContainer0.<>p__Site1 == 
null
08.         { 
09.             <Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, 
object>>. 
10. Create(
new CSharpInvokeMemberBinder(CSharpCallFlags.None, 
"
ToString
"
typeof(Program), 
11
null
new CSharpArgumentInfo[] { 
new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, 
null) })); 
12.         } 
13.         <Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, d); 
14.     } 
15
//
 Nested Types
 
16.     [CompilerGenerated] 
17.     
private 
static 
class <Main>o__SiteContainer0 
18.     { 
19.         
//
 Fields
 
20.         
public 
static CallSite<Action<CallSite, 
object>> <>p__Site1; 
21.     } 
22. }

06行先把赋值给dynamic的值赋给object类型,检查编译器生成的静态类<Main>o__SiteContainer0的静态字段<>p__Site1是否为null,如果是,则对其赋值,赋值的内容在这里不详细研究。然后调用<>p__Site1进行操作。

这里会发现两个问题:赋值给dynamic的值赋给object类型,对于值类型会不会执行同样的操作,会执行装箱操作吗;编译器生成的静态类<Main>o__SiteContainer0的静态字段<>p__Site1应该是缓存作用。这两个问题稍后验证。


1)对值类型进行的操作

如下代码:

using System; 
namespace  .Net.TestDynamic 
    
class Program 
    { 
        
static 
void Main() 
        { 
            dynamic d = 
5
            d.ToString(); 
        } 
    } 
}

反编译代码:

internal 
class Program 
    
//
 Methods
 
    
private 
static 
void Main() 
    { 
        
object d = 
5
        
if (<Main>o__SiteContainer0.<>p__Site1 == 
null
        { 
            <Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, 
object>>. 
Create(
new CSharpInvokeMemberBinder(CSharpCallFlags.None, 
"
ToString
"
typeof(Program), 
null
new CSharpArgumentInfo[] { 
new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, 
null) })); 
        } 
        <Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, d); 
    } 
    
//
 Nested Types
 
    [CompilerGenerated] 
    
private 
static 
class <Main>o__SiteContainer0 
    { 
        
//
 Fields
 
        
public 
static CallSite<Action<CallSite, 
object>> <>p__Site1; 
    } 
}

可见确实对值类型进行了装箱操作,效率可想而知。


2)编译器生成的缓存类
代码如下:

using System; 
namespace  .Net.TestDynamic 
    
class Program 
    { 
        
static 
void Main() 
        { 
            dynamic d = 
5
            d.ToString(); 
            d.ToString(); 
        } 
    } 
}

反编译的代码:

internal 
class Program 
    
//
 Methods
 
    
private 
static 
void Main() 
    { 
        
object d = 
5
        
if (<Main>o__SiteContainer0.<>p__Site1 == 
null
        { 
            <Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, 
object>>.Create(
new CSharpInvokeMemberBinder(CSharpCallFlags.None,
"
ToString
"
typeof(Program), 
null
new CSharpArgumentInfo[] { 
new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, 
null) })); 
        } 
        <Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, d); 
        
if (<Main>o__SiteContainer0.<>p__Site2 == 
null
        { 
            <Main>o__SiteContainer0.<>p__Site2 = CallSite<Action<CallSite, 
object>>.Create(
new CSharpInvokeMemberBinder(CSharpCallFlags.None,
"
ToString
"
typeof(Program), 
null
new CSharpArgumentInfo[] { 
new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, 
null) })); 
        } 
        <Main>o__SiteContainer0.<>p__Site2.Target(<Main>o__SiteContainer0.<>p__Site2, d); 
    } 
    
//
 Nested Types
 
    [CompilerGenerated] 
    
private 
static 
class <Main>o__SiteContainer0 
    { 
        
//
 Fields
 
        
public 
static CallSite<Action<CallSite, 
object>> <>p__Site1; 
        
public 
static CallSite<Action<CallSite, 
object>> <>p__Site2; 
    } 
}

代码调用了ToString方法,但编译器生成了两份缓存。


如果是在循环中:

代码:

using System; 
namespace  .Net.TestDynamic 
    
class Program 
    { 
        
static 
void Main() 
        { 
            dynamic d = 
5
            
for (
int i = 
0; i < 
100; i++) 
            { 
                d.ToString(); 
            } 
        } 
    } 
}

反编译代码:

internal 
class Program 
    
//
 Methods
 
    
private 
static 
void Main() 
    { 
        
object d = 
5
        
for (
int i = 
0; i < 
100; i++) 
        { 
            
if (<Main>o__SiteContainer0.<>p__Site1 == 
null
            { 
                <Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, 
object>>.Create(
new CSharpInvokeMemberBinder(CSharpCallFlags.None,
"
ToString
"
typeof(Program), 
null
new CSharpArgumentInfo[] { 
new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, 
null) })); 
            } 
            <Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, d); 
        } 
    } 
    
//
 Nested Types
 
    [CompilerGenerated] 
    
private 
static 
class <Main>o__SiteContainer0 
    { 
        
//
 Fields
 
        
public 
static CallSite<Action<CallSite, 
object>> <>p__Site1; 
    } 
}

可见在循环中,相同的操作做了一次缓存;但非循环环境下,调用一次会缓存一次,猜测原因是,重复调用一个方法的次数不会太多,并且很多情况准确查找起来比较困难。

(以上代码在VS2010Beta1下测试通过)

URL: 

Author: Zhenxing Zhou

转载地址:http://jhrmb.baihongyu.com/

你可能感兴趣的文章
百度搜索引擎使用指南(转)
查看>>
专家观点:安全成交换机的基本功能(转)
查看>>
树型结构在ASP中的简单解决(转)
查看>>
解决玩游戏时显卡卡屏现象(转)
查看>>
移动通信概要(转)
查看>>
CMD命令全集(转)
查看>>
深度探索C++对象模型 ( 第四部分 )(转)
查看>>
MySQL中的SQL特征(转)
查看>>
使用JBuilder和WTK2.2搭建MIDP1.0和MIDP2.0开发环境(转)
查看>>
Symbian命名规则(翻译)(转)
查看>>
windows server 2003的设置使用(转)
查看>>
优化Win2000的NTFS系统(转)
查看>>
IE漏洞可使黑客轻易获取私人信息(转)
查看>>
脱机备份与恢复实战(转)
查看>>
WLINUX下的DNS服务器设置(转)
查看>>
游戏引擎剖析(二)(转)
查看>>
sms发mms C语言源码(转)
查看>>
窝CDMA网络中移动IP接入Internet(转)
查看>>
实现MMS增值业务的关键技术(转)
查看>>
Vista被破解 一个小程序可成功激活(转)
查看>>