上一篇 背后的故事之 – 快乐的lambda表达式(一)我们由浅入深的分析了一下lambda表达式。知道了它和委托以及普通方法的区别,并且通过测试对比他们之间的性能,然后我们通过il代码深入了解了lambda表达式,以及介绍了如何在.net中用lambda表达式来实现javascript中流行的一些模式。
今天,我们接着来看lambda表达式在.net中还有哪些新鲜的玩法。
lambda表达式玩转多态
lambda如何实现多态?我们用抽象类和虚方法了,为什么还要用lambda这个玩意?且看下面的代码:
class mybaseclass
{
public action someaction { get; protected set; }
public mybaseclass()
{
someaction = () =>
{
//do something!
};
}
}
class myinheritedclass : mybaseclass
{
public myinheritedclass()
{
someaction = () => {
//do something different!
};
}
}
我们的基类不是抽象类,也没有虚方法,但是把属性通过委托的方式暴露出来,然后在子类中重新为我们的someaction赋予一个新的表达式。这就是我们实现多态的过程,当然父类中的someaction的set有protected的保护级别,不然就会被外部随易修改了。但是这还不完美,父类的someaction在子类中被覆盖之后,我们彻底访问不到它了,要知道真实情况是我们可以通过base来访问父类原来的方法的。接下来就是实现这个了
class mybaseclass
{
public action someaction { get; private set; }
stack<action> previousactions;
protected void addsomeaction(action newmethod)
{
previousactions.push(someaction);
someaction = newmethod;
}
protected void removesomeaction()
{
if(previousactions.count == 0)
return;
someaction = previousactions.pop();
}
public mybaseclass()
{
previousactions = new stack<action>();
someaction = () => {
//do something!
};
}
}
上面的代码中,我们通过addsomeaction来实现覆盖的同时,将原来的方法保存在previousactions中。这样我们就可以保持两者同时存在了。
大家知道子类是不能覆盖父类的静态方法的,但是假设我们想实现静态方法的覆盖呢?
void main()
{
var mother = hotdaughter.activator().message;
//mother = "i am the mother"
var create = new hotdaughter();
var daughter = hotdaughter.activator().message;
//daughter = "i am the daughter"
}
class coolmother
{
public static func<coolmother> activator { get; protected set; }
//we are only doing this to avoid null references!
static coolmother()
{
activator = () => new coolmother();
}
public coolmother()
{
//message of every mother
message = "i am the mother";
}
public string message { get; protected set; }
}
class hotdaughter : coolmother
{
public hotdaughter()
{
//once this constructor has been "touched" we set the activator ...
activator = () => new hotdaughter();
//message of every daughter
message = "i am the daughter";
}
}
这里还是利用了将lambda表达式作为属性,可以随时重新赋值的特点。当然这只是一个简单的示例,真实项目中并不建议大家这么去做。
方法字典
实际上这个模式我们在上一篇的返回方法中已经讲到了,只是没有这样一个名字而已,就算是一个总结吧。故事是这样的,你是不是经常会写到switch-case语句的时候觉得不够优雅?但是你又不想去整个什么工厂模式或者策略模式,那怎么样让你的代码看起来高级一点呢?
public action getfinalizer(string input)
{
switch
{
case "random":
return () => { /* ... */ };
case "dynamic":
return () => { /* ... */ };
default:
return () => { /* ... */ };
}
}
//-------------------变身之后-----------------------
dictionary<string, action> finalizers;
public void buildfinalizers()
{
finalizers = new dictionary<string, action>();
finalizers.add("random", () => { /* ... */ });
finalizers.add("dynamic", () => { /* ... */ });
}
public action getfinalizer(string input)
{
if(finalizers.containskey(input))
return finalizers[input];
return () => { /* ... */ };
}
好像看起来是不一样了,有那么一点味道。但是一想是所有的方法都要放到那个buildfinalizers里面,这种组织方法实在是难以接受,我们来学学插件开发的方式,让它自己去找所有我们需要的方法。
static dictionary<string, action> finalizers;
// 在静态的构造函数用调用这个方法
public static void buildfinalizers()
{
finalizers = new dictionary<string, action>();
// 获得当前运行程序集下所有的类型
var types = assembly.getexecutingassembly().gettypes();
foreach(var type in types)
{
// 检查类型,我们可以提前定义接口或抽象类
if(type.issubclassof(typeof(mymotherclass)))
{
// 获得默认无参构造函数
var m = type.getconstructor(type.emptytypes);
// 调用这个默认的无参构造函数
if(m != null)
{
var instance = m.invoke(null) as mymotherclass;
var name = type.name.remove("mother");
var method = instance.mymethod;
finalizers.add(name, method);
}
}
}
}
public action getfinalizer(string input)
{
if(finalizers.containskey(input))
return finalizers[input];
return () => { /* ... */ };
}
如果要实现插件化的话,我们不光要能够加载本程序集下的方法,还要能随时甚至运行时去加载外部的方法,请继续往下看:
internal static void buildinitialfinalizers()
{
finalizers = new dictionary<string, action>();
loadplugin(assembly.getexecutingassembly());
}
public static void loadplugin(assembly assembly)
{
var types = assembly.gettypes();
foreach(var type in types)
{
if(type.issubclassof(typeof(mymotherclass)))
{
var m = type.getconstructor(type.emptytypes);
if(m != null)
{
var instance = m.invoke(null) as mymotherclass;
var name = type.name.remove("mother");
var method = instance.mymethod;
finalizers.add(name, method);
}
}
}
}
现在,我们就可以用这个方法,给它指定程序集去加载我们需要的东西了。
最后留给大家一个问题,我们能写递归表达式么?下面的方法如果用表达式如何写呢?
int factorial(int n)
{
if(n == 0)
return 1;
else
return n * factorial(n - 1);
}
以上就是背后的故事之 - 快乐的lambda表达式(二)的内容。
