本文主要旨在与网友分享.Net4.0的Dynamic 对Duck Type 的支持。
一、.net4.0主要新特性
.Net4.0在.Net3.5基础上新增的主要特性有:可选参数、命名参数和Dynamic。具体请阅生鱼片的这篇博文。这里我们着重讲解C#4.0的Dynamic特性,对于其他特性大家可以在VS2010内尝试一下。总之.Net在不断进步中。
二、ExpandoObject普通应用
ExpandoObject 类,“需引用System.Dynamic命名空间” 。请看以下代码:
1
dynamic Customer
=
new
ExpandoObject();
2
Customer.Name
=
"
Lucy
"
;
3
Customer.Age
=
20
;
4
Customer.Female
=
true
;
5
Console.WriteLine(Customer.Name
+
Customer.Age
+
Customer.Female);
6
Console.ReadKey();
输出<< Lucy20True,这里已经类似javascript 的var obj ={}; obj.Name ='lucy'。但又不完全相同,因为不能在clr运行时动态生成属性或者方法。但至少比.NET3.5先进了。
三、Dynamic 动态类型对DuckType 的支持
1.前不久在园子里面看见了thinking的这篇博文,文中有这么一段代码:
static
class
Calculator {
public
static
T Add
<
T
>
(T t1, T t2) { dynamic d1
=
t1; dynamic d2
=
t2;
return
(T)(d1
+
d2); } }
public
static
void
Main(
string
[] args){
int
i
=
Calculator.Add(
1
,
2
);
double
d
=
Calculator.Add(
1.1
,
2.2
);
string
s
=
Calculator.Add(
"
abc
"
,
"
def
"
); Console.WriteLine(i
+
"
"
+
d
+
"
"
+
s); }
输出:
>>3 3.3 abcdef
作者在文中指出了以上C#代码是为了通过动态类型来实现基于duck typing的泛型参数约束。
为了在C#支持Duck Type还有一个重要的.Net4.0特性有必要提及到,这也是本文重点讨论的内容。
它就是:“DynamicObject” 该类位于System.Dynamic 命名空间下。在VS2010内就可以看见该类的成员列表,截图如下:
所属方法都是虚方法,我们可以重写这些虚方法。这里主要看TryInvokeMember()方法。这个方法VS2010给出了详细的描述。
根据VS2010注释,由于我的VS2010是英文版的,这里就不贴出英文注释了。简单介绍一下如何使用这个方法:假设我们一个类OurClass它继承了DynamicObject 这个Class。OurClass中有一个方法OurMethod()。接着在OurClass 类中 重写 TryInvokeMember这个基类虚方法。以上设置完后以后只要OurClass 的OurMethod方法一旦被调用都先执行一下重写后的
TryInvokeMember()方法。也许您会问这样到底有何用途?OK!请先看javascript这段代码片段:
1
function
tryInvokeMember(obj) {
2
if
(obj
&&
typeof
obj.ourMethod
===
"
function
"
) {
3
return
obj.ourMethod();
4
}
5
alert(
'
未找到!
'
);
6
return
null
;
7
}
8
9
var
ourObj1
=
{};
10
ourObj1.Method
=
function
() {
11
alert(
'
111
'
);
12
};
13
14
var
ourObj2
=
{};
15
ourObj2.ourMethod
=
function
() {
16
alert(
'
已经找到ourMethod并且执行
'
);
17
};
18
19
tryInvokeMember(ourObj1);
20
tryInvokeMember(ourObj2);
大家读完
这段
js代码后应该会明白为什么我要重点讨论C#4.0中的DynamicObject了吧?真正的目的就是:在DuckType 类(鸭子) 方法(鸭子叫)执行之前,我们要判断对象的类是否是具备鸭子叫的功能?如果不具备就不应该执行,否则程序势必会抛出异常。C#中如何实现呢?步骤如下:
1、建立DynamicAnimal 类继承DynamicObject类,并且重写TryInvokeMember虚方法:
public
class
DynamicAnimal : DynamicObject {
public
override
bool
TryInvokeMember(InvokeMemberBinder binder,
object
[] args,
out
object
result) {
bool
success
=
base
.TryInvokeMember(binder, args,
out
result);
//
如果方法不存在,请将result 这个out参数赋值为null
if
(
!
success) result
=
null
;
//
如果这个地方返回false 将会引发异常
return
true
; } }
2、建立两个DuckType类,分别为Duck 和 Human:
1
public
class
Duck : DynamicAnimal
2
{
3
public
string
Quack()
4
{
5
return
"
鸭子嘛,就Quack吧!
"
;
6
}
7
}
8
public
class
Human : DynamicAnimal
9
{
10
public
string
Talk()
11
{
12
return
"
人类是用Talk,而不是Quack
"
;
13
}
14
}
3、在Console 内 建立DuckType的调用方法:
1
public
static
string
DoQuack(dynamic animal)
2
{
3
string
result
=
animal.Quack();
4
return
result
??
"
...人类当然不会鸭叫...
"
;
5
}
4、Console 内 Main方法调用:
1
static
void
Main(
string
[] args)
2
{
3
var duck
=
new
Duck();
4
var cow
=
new
Human();
5
Console.WriteLine(
"
鸭子是Quack
"
);
6
Console.WriteLine(DoQuack(duck));
7
Console.WriteLine(
"
人类是talk
"
);
8
Console.WriteLine(DoQuack(cow));
9
Console.ReadKey();
10
}
程序
执行结果
如下: