我遇到的问题是我需要做大约40多次转换才能将松散类型的信息转换为存储在db,xml文件等中的强类型信息.
我打算用元组标记每种类型,即这样的转换形式:
host.name.string:host.dotquad.string
Run Code Online (Sandbox Code Playgroud)
它将提供从输入到输出表单的转换.例如,名称存储在字符串类型的主机字段中,输入转换为字符串类型的dotquad表示法并存储回主机字段.更复杂的转换可能需要几个步骤,每个步骤都是通过方法调用完成的,因此方法链接.
进一步检查上面的例子,元组'host.name.string'的字段主机名为www.domain.com.执行DNS查找以将域名转换为IP地址.应用另一种方法将DNS查找返回的类型更改为string类型的dotquad的内部类型.对于这种转换,有4个单独的方法被称为从一个元组转换为另一个元组.其他一些转换可能需要更多步骤.
理想情况下,我想了解一个在运行时如何构造方法链的小例子.开发时间方法链接相对简单,但需要页面和代码页来覆盖所有可能性,并且需要40多次转换.
我想做的一种方法是,在启动时解析元组,并将链写出到一个程序集,编译它,然后使用反射来加载/访问.它真的很丑陋,否定了我希望获得的性能提升.
我正在使用Mono,所以没有C#4.0
任何帮助,将不胜感激.鲍勃.
这是一个使用 LINQ 表达式的快速但肮脏的解决方案。您已经表明您想要 C# 2.0,这是 3.5,但它确实可以在 Mono 2.6 上运行。方法链接有点hacky,因为我不完全知道你的版本是如何工作的,所以你可能需要调整表达式代码以适应。
真正的魔力确实发生在Chainer类中,它采用代表子类的字符串集合MethodChain。采取这样的集合:
{
"string",
"string",
"int"
}
Run Code Online (Sandbox Code Playgroud)
这将生成一个像这样的链:
new StringChain(new StringChain(new IntChain()));
Run Code Online (Sandbox Code Playgroud)
Chainer.CreateChain将返回一个调用 的 lambda MethodChain.Execute()。因为Chainer.CreateChain使用了一点反射,所以速度很慢,但它只需要为每个表达式链运行一次。lambda 的执行速度几乎与调用实际代码一样快。
希望您能将其融入您的架构中。
public abstract class MethodChain {
private MethodChain[] m_methods;
private object m_Result;
public MethodChain(params MethodChain[] methods) {
m_methods = methods;
}
public MethodChain Execute(object expression) {
if(m_methods != null) {
foreach(var method in m_methods) {
expression = method.Execute(expression).GetResult<object>();
}
}
m_Result = ExecuteInternal(expression);
return this;
}
protected abstract object ExecuteInternal(object expression);
public T GetResult<T>() {
return (T)m_Result;
}
}
public class IntChain : MethodChain {
public IntChain(params MethodChain[] methods)
: base(methods) {
}
protected override object ExecuteInternal(object expression) {
return int.Parse(expression as string);
}
}
public class StringChain : MethodChain {
public StringChain(params MethodChain[] methods):base(methods) {
}
protected override object ExecuteInternal(object expression) {
return (expression as string).Trim();
}
}
public class Chainer {
/// <summary>
/// methods are executed from back to front, so methods[1] will call method[0].Execute before executing itself
/// </summary>
/// <param name="methods"></param>
/// <returns></returns>
public Func<object, MethodChain> CreateChain(IEnumerable<string> methods) {
Expression expr = null;
foreach(var methodName in methods.Reverse()) {
ConstructorInfo cInfo= null;
switch(methodName.ToLower()) {
case "string":
cInfo = typeof(StringChain).GetConstructor(new []{typeof(MethodChain[])});
break;
case "int":
cInfo = typeof(IntChain).GetConstructor(new[] { typeof(MethodChain[]) });
break;
}
if(cInfo == null)
continue;
if(expr != null)
expr = Expression.New(cInfo, Expression.NewArrayInit( typeof(MethodChain), Expression.Convert(expr, typeof(MethodChain))));
else
expr = Expression.New(cInfo, Expression.Constant(null, typeof(MethodChain[])));
}
var objParam = Expression.Parameter(typeof(object));
var methodExpr = Expression.Call(expr, typeof(MethodChain).GetMethod("Execute"), objParam);
Func<object, MethodChain> lambda = Expression.Lambda<Func<object, MethodChain>>(methodExpr, objParam).Compile();
return lambda;
}
[TestMethod]
public void ExprTest() {
Chainer chainer = new Chainer();
var lambda = chainer.CreateChain(new[] { "int", "string" });
var result = lambda(" 34 ").GetResult<int>();
Assert.AreEqual(34, result);
}
}
Run Code Online (Sandbox Code Playgroud)