切换替代/可扩展编程良好实践?

时间:2013-05-24 02:57:01

标签: c# linq switch-statement extension-methods mef

首先,我的问题的一些背景;我正在用C#编写模块化的服务器/客户端类型程序。我有一个线程处理系统来处理来自客户端队列的数据包,这是我正在使用的一个例子:

public delegate object ProcessPacket(object data);
public class QueueHandler
{
    //Awesome functions/variables
    private void Proccess()
    {
        object retPacket = client.ProcessPacket(obj);
        NetworkCommon.Sendpacket(retPacket, _clientSocket);
    }
    //Even more awesome functions.
}
//Client ProcessPacket delegate
private static object ProcessPacket(object packet)
{
    ReturnPacket ret = new ReturnPacket();
    switch (packet.Command)
    {
        case Command.Login:
            //Do login stuff
            break;
        case Command.Foo;
            //Foo stuff
            break;
        case Command.Bar;
            //Bar stuff
            break;
        default:
            ret.Responce = CommandResponce.CommandNotRecognized;
    }
    return ret;
}

正如您所猜测的,这不是非常可扩展的,并且很难通过添加功能进行管理。我的问题是,这是一个更实用的方法吗?

我想到了List<string,TProcessCommand>种类,为函数分配了一个自定义属性,然后在启动时使用反射构建该列表。但是觉得管理起来可能仍然很麻烦,并且每次启动都可能需要不必要的处理能力。

我考虑的另一个选择是使用MEF,但是我很难在不使用Linq的情况下如何识别命令,我不熟悉。

[[编辑]] 我刚刚在MEF example中注意到,ExportMetadataAttribute被使用了。我会用我的代码给出一个镜头。

1 个答案:

答案 0 :(得分:2)

我会创建一个界面,让我们称之为

public interface ICommandManager
{
    ReturnPacket DoYourStuff();
}

和这样的属性:

public class HandlesAttribute : Attribute
{
    public Command HandlesCommand { get; private set; }

    public HandlesAttribute(Command cmd)
    {
        this.HandlesCommand = cmd;
    }
}

您现在可以为命令处理实现类,并为它们提供必要的属性。

[Handles(Command.Login)]
public class LoginCommandManager : ICommandManager
{
    public ReturnPacket DoYourStuff()
    {
        var ret = new ReturnPacket();
        /* Do some stuff */
        return ret;
    }
}

现在你可以使用例如初始化这些类的对象。反思(只需查找实现ICommandManager的类型),而无需对switch进行硬编码,只需在有新命令时创建新的ICommandManager实现。

或者更奇特的方式,没有使用 Ninject

的属性
public class LoginCommandManager : ICommandManager
{
    public ReturnPacket DoYourStuff()
    {
        var ret = new ReturnPacket();
        /* Do some stuff */
        return ret;
    }
}

现在您可以使用 Ninject 来解决您的问题。使用此代码注册绑定。

kernel.Bind<ICommandManager>()
    .To<LoginCommandManager>()
    .WithMetadata("Command", Command.Login);
/* ... */

稍后您可以通过以下方式解决此问题:

kernel.Get<ICommandManager>(metadata => 
    packet.Command == metadata.Get<Command>("Command"));