在运行时创建和存储方法链的方法

sco*_*eep 6 chaining c#-2.0

我遇到的问题是我需要做大约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

任何帮助,将不胜感激.鲍勃.

Igo*_*aka 1

这是一个使用 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)