方法工厂 - 案例与反思

Sea*_*ard 7 c# reflection factory switch-statement

前几天我遇到了一些代码,我想知道这是否是最好的方法.我们有一个方法,它根据传入的字符串从一些Web表单数据中获取一个字符串对象.当前,它使用反射来计算要采取的操作,但我想知道switch语句是否会更好.

例:

编辑:我为Lucerno指出的代表添加了第三个选项

public class ObjectManipulator
{
    private void DoX(object o) { }
    private void DoY(object o) { }
    private void DoZ(object o) { }

    public void DoAction(string action, object o)
    {
        switch (action)
        {
            case "DoX":
                DoX(o);
                break;
            case "DoY":
                DoY(o);
                break;
            case "DoZ":
                DoZ(o);
                break;
            default:
                throw new Exception(string.Format(
                    "Cannot locate action:{0}", action));
        }
    }

    public void DoActionViaReflection(string action, object o)
    {
        MethodInfo method = typeof(ObjectManipulator).
            GetMethod(action, new Type[] { typeof(object) });
        if (method == null)
        {
            throw new Exception(string.Format(
                "Cannot locate action:{0}", action));
        }
        else
        {
            method.Invoke(this, new object[] { o });
        }
    }
    private Dictionary<string, Action<object>> _methods;
    public ObjectManipulator()
    {
        _methods = new Dictionary<string, Action<object>>()
        {
            {"DoX", o => DoX(o)},
            {"DoY", o => DoY(o)},
            {"DoZ", o => DoZ(o)}
        };
    }
    public void DoActionViaDelegates(string action, object o)
    {
        if (!_methods.ContainsKey(action))
        {
            throw new Exception(string.Format(
                "Cannot locate action:{0}", action));
        }
        else
        {
            _methods[action](o);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

第一个例子使用了开关,正如你所看到的那样可能非常冗长.第二个更短,但使用反射,我知道有些人像瘟疫一样避免.

一种方法的表现会明显好于另一种吗?

如果有100个不同的动作而不是仅仅3个,性能会改变吗?

如果你正在阅读它,你更愿意在你的代码中看到哪一个?

Luc*_*ero 5

第一种情况几乎总是会更快。但是,它的性能来自于它可以在编译时提前绑定的事实,但这也是它的最大缺点:例如,这种方法无法处理动态加载的程序集,并且它更容易出错。命令性的而不是陈述性的。(例如,忘记新实施的动作可能很快就会发生。)

我通常的方法是在发现时使用反射来实现这种模式,而在调用时使用委托。这为您提供了反射方法的灵活性,其性能非常接近于早期绑定方法。

  • 发现阶段:使用反射来查找成员(使用属性,接口,签名和/或编码约定)。在您的情况下,您始终具有相同的签名,因此要使用的委托将是Action<object>。将这些成员添加到Dictionary<string, Action<object>>实例,从MethodInfousing 创建一个委托CreateDelegate()

  • 调用阶段:通过其键获取委托并调用它,这非常简单(在此假设字典被称为methods):methods[action](o)