1: Override VS. OverloadíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi多态可以说是面向对象世界中一件锋利的武器, 封装变化是它的能力的体现。但是你听说过几种多态?íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiSimple Polymorphism :the object whose method is called is decided run-time.íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
multi- polymorphism :the object which method is called íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
is decided upon the type of the argumentíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
如果你对这两句描述不是很清楚, 那你知道override和overload吗?Simple Polymorphism 就意味使用了override, 而multi- polymorphism则意味着使用了overload
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi前者可能你比较熟悉,后者呢?两者又有什么不同?什么情况下我们会需要后者呢?你见过它们同时出现吗?
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi多态是用于封装变化的,比如常见的那个ShapeíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
Draw的例子。Client不用考虑具体是哪个Shape,通过多态自然能调用到相应的那个Shape的Draw方法(whose method)。但是这时我们只有一个变化的对象――Shape。 如果画的地方也变呢?比如我可以画在屏幕上, 也可以画到打印机上。现在我们有两个同时会变的因素, 那么Draw方法又通过什么来实现封装变化呢? Simple Polymorphism 显然是不够用了,multi- polymorphism 自然也该出场了。
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi就从一个游戏开始吧,在这个游戏中有一个怪物开门的场景。怪物有很多种,本游戏的出场人物包括了矮人和泰坦,门也有两种 :一种普通的木头门, 还有就是很重的铁门。
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi现在怪物和门登场了…
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiinterface Monster {}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
class Drawf : Monster {}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
class Giant : Monster {}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
class DooríS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public virtual void OpenBy(Monster monster)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
Console.WriteLine("Who are u?");íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public virtual void OpenBy(Drawf dwarf)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
Console.WriteLine("It's slowly opened");íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public virtual void OpenBy(Giant giant)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
Console.WriteLine("It's just easily broken...Crasp!");íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
class HeavyDoor : DooríS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public override void OpenBy(Drawf dwarf)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
Console.WriteLine("It won't open");íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public override void OpenBy(Giant giant)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
Console.WriteLine("It's slowly opened");íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi这里为了同时封装两种变化(怪物和门),我也同时使用了override 和 overloadíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
。
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi接着怪物开始开门了…
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
class GameíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
static void Main()íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
Door ironDoor = new HeavyDoor();íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
ironDoor.OpenBy(new Drawf());íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
ironDoor.OpenBy(new Giant());íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
答案也很明显
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi这个例子同时应用了两种多态,íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
但是却只体现了第一种多态封装变化的效果!如何将两者都体现出来?看下面的测试
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiclass GameíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
static void Main()íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
Door ironDoor = new HeavyDoor();íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
List<Monster> monsters = new List<Monster>();íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
monsters.Add(new Drawf());íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
monsters.Add(new Giant());
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
foreach (Monster m in monsters)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
ironDoor.OpenBy(m);íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi现在你能猜到结果吗?仔细想想别急着看答案。
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
很正常?Ok. 你可以直接去看下一节的内容了,如果猜错了请先复习一下下面的基础知识吧.
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi你肯定听说过所谓的”
动态绑定”
,通常意义上的多态也就是通过它实现的,简而言之――the object whose method is called is decided run-time .
重点就在于这个run-time .
而第二种多态――the object which method is called íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
is decided upon the type of the argument 这里面可没有出现run-time 倒不是说它不支持,而是不一定,不过在c++, java
,c#
中都不支持。 现在你可以理解为什么前面的答案出乎你的意料了。因为后者方法是静态绑定的, 也就是在编译期就确定了将要执行哪个Draw方法,而在编译期,编译器显然只能将monsters
集合中的对象判断为Monster
类型,从而去执行 void OpenBy(Monster monster)方法。íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi2: Visitor Pattern without Double DispatchíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
通过上一节的例子和解释, 你应该对override, overload 以及他们各自的绑定机制――动态绑定和静态绑定有所了解。
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
接下来看一个更具实际意义的例子。假设我想做一个计算器,那么肯定要先建立一个表达式系统。
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiabstract class ExpressioníS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public abstract int Evaluate();íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
class ConstantExpression : ExpressioníS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{ íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
int constant;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public override int Evaluate()íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
return constant;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
class SumExpression : ExpressioníS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
Expression left, right;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public override int Evaluate()íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
return left.Evaluate() + right.Evaluate();íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi这里利用多态很好的实现了表达式计算的任务。但是把计算的功能放在表达式中并不是一个良好的设计,随着表达式的类型越来越复杂,可能我们需要对表达式进行语法分析,进行类型检查, 设置判断表达式中有多少个常量,多少个变量。如果把这些功能都放在表达式中,一方面不符合责任分离的原则,二来一旦有了新的功能需求我们就要修改所有的表达式对象,维护的恶梦就这样开始了。
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
于是我就想到了用Visitor模式将计算的功能从Express类中分离出来。(将过多没有密切联系的功能从原来的对象中脱离出来,避免对象过于庞大,这是使用Visitor模式的最重要的原因)。下面是代码实现,请耐心看完。íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiabstract class Expression { }
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
class ConstantExpression : ExpressioníS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
private int constant;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public int ConstantíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
get { return constant; }íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public ConstantExpression(int con)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
constant = con;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
class SumExpression : ExpressioníS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
private Expression left, right;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public Expression RightíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
get { return right; }íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public Expression LeftíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
get { return left; }íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public SumExpression(Expression left, Expression right)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
this.left = left;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
this.right = right;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
class EvaluateVisitoríS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public int Visit(ConstantExpression e)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
return e.Constant;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public int Visit(SumExpression e)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
return Visit(e.Left) + Visit(e.Right);íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
class ProgramíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
static void Main()íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
ConstantExpression constExp = new ConstantExpression(10); íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
SumExpression sumExp = new SumExpression(new ConstantExpression(1),íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
new ConstantExpression(1));
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
EvaluateVisitor evalVisitor = new EvaluateVisitor();íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
Console.WriteLine(evalVisitor.Visit(constExp));íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
Console.WriteLine(evalVisitor.Visit(sumExp));íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi

íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi看完上面的代码,可能很多人会有疑问了。 你这里用的是什么Visitor模式?Accept方法呢?IVisitor接口呢? 怎么Visit方法还有返回值?怎么和《Design Pattern》上的Visitor模式完全不是一回事?
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi为什么要和书上的一样呢?你难道没发现以上的疑问都体现了这个版本的优点?没有Accpet方法意味着在最初的设计中我根本不需要为以后是否需要使用Visitor模式做考虑。没有IVisitor接口,意味着我可以随意的定义我的Visit方法,而不需要一个统一的形式,比如这里为了计算方便我让Visit方法有了返回值。既然这个版本的Visitor模式这么好,怎么Gof没有想到? 呵呵,露馅了,因为上面的代码根本无法通过编译íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
class EvaluateVisitoríS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public int Visit(ConstantExpression e)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
return e.Constant;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public int Visit(SumExpression e)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
return Visit(e.Left) + Visit(e.Right);íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi联系第一节介绍的内容,你就会发现尽管e.Left在运行期是ConstantExpression类型,但是由于Overload的方法是静态绑定的, 而在编译期e.Left是Express类型, 但是我们根本没有提供Visit(Expression e)这样的方法, 编译自然出错了。íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi3: Visitor Pattern with Double DispatchíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
回过头去看看传统的Visitor模式又是什么样子的呢?
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiabstract class ExpressioníS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public abstract void Accept(Visitor v); íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiclass ConstantExpression : ExpressioníS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
private int constant;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public int ConstantíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
get { return constant; }íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public ConstantExpression(int con)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
constant = con;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public override void Accept(Visitor v)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
v.Visit(this);íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiclass SumExpression : ExpressioníS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
private Expression left, right;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public Expression RightíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
get { return right; }íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public Expression LeftíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
get { return left; }íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public SumExpression(Expression left, Expression right)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
this.left = left;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
this.right = right;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public override void Accept(Visitor v)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
v.Visit(this);íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiinterface VisitoríS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
void Visit(ConstantExpression e);íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
void Visit(SumExpression e);íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiclass EvaluateVisitor : VisitoríS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
private int result;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public int Result { get { return result; } }
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public void Visit(ConstantExpression e)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
result=e.Constant;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public void Visit(SumExpression e)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
e.Left.Accept(this);íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
int lefeRe = this.result;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
e.Right.Accept(this);íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
int rightRe = this.result;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
result = lefeRe + rightRe;íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiclass ProgramíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
static void Main()íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
ConstantExpression constExp = new ConstantExpression(10);íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
SumExpression sumExp = new SumExpression(new ConstantExpression(1),íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
new ConstantExpression(1));
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
EvaluateVisitor evalVisitor = new EvaluateVisitor();íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
constExp.Accept(evalVisitor);íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
Console.WriteLine(evalVisitor.Result);íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
sumExp.Accept(evalVisitor);íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
Console.WriteLine(evalVisitor.Result);íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
可以看出这张图和前者相比,复杂了许多。但是只有这样我们才能在C#这种不直接支持Double Dispatch的语言中实现Visitor模式。
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
怎么解决的呢? 道理很简单就是用Twice Single Dispatch来模拟Double Dispatch。最能体现Twice Single Dispatch的代码如下:íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public override void Accept(Visitor v)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
v.Visit(this);íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi其一般形式为:íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
public void MethodA(A a)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
a.MethodB(this);íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
其实说到底,不支持Double Dispatch就是由于不支持方法参数的动态绑定, 那我们就通过两次的O虚方法的动态绑定来模拟它。所以 a 又从方法参数变成了虚方法的拥有者,并把this作为参数传入。
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
明白了以上的道理,你也就知道为什么完全同样的Accpet方法没有被提到它们的基类中――-因为方法参数的类型是在编译期决定的。
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
看到了传统Visitor模式的解决思路,对于文章一开始的小游戏所引起的问题也就不难解决了。íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi4: Visitor Pattern with Reflection
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰiíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
再来看看第二节的模型,实在是很漂亮,让我不忍放弃。Gof 也想不出什么好的办法,只能无奈的采用了第三节的复杂模型。 不过,你记得吗?《Design Pattern》那本书可是十年前的作品。十年前可没有什么元数据,自然也没有Reflection。
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi通过反射我完全可以在运行期从函数参数中找回失去的类型信息。
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi我只要在第二节的EvaluateVisitor类中添加如下的一个方法,就万事告吉了。
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰipublic int Visit(Expression e)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
{íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
Type[] types = new Type[] { e.GetType() };íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
MethodInfo mi = this.GetType().GetMethod("Visit", types);íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
if (mi==null)íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
throw new Exception("UnSupported!");íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
elseíS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
return (int)mi.Invoke(this,new object[]{e});íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
}íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi 这个方法我个人是比较满意了,你觉得呢?
íS
¾
£Hª;www.netcsharp.cnÅÑà×AŰi