使用通用类型“切换类型”是一个坏主意吗?

时间:2013-08-27 13:10:03

标签: c# generics pattern-matching

C# - Is there a better alternative than this to 'switch on type'?

相关

我需要'切换类型':给定T类型的参数,找到并执行void Method (T param)形式的方法。

这可以通过switch语句或Dictionary<Type, Action>来解决,但是我想避免在这种情况下必需的强制转换。

我找不到上述(或类似)问题中提到的以下方法:

  1. 创建一个充当集合的静态泛型类型:

    public static class Commands<T> {
      public static Action<T> Handler;
    }
    
  2. 创建一个使用该类型的存储库,如类型安全字典:

    public class CommandRepository {
        public void Register<T>(Action<T> handler) {
            Commands<T>.Handler = handler;
        }
    
        public void Run<T>(T parameter) {
            // null checks etc.
            Commands<T>.Handler(parameter);
        }
    }
    
  3. 示例用法:

    public void CreateUser(CreateUserParams p) {
        Console.WriteLine("Creating " + p.Name);
    }
    
    // ...
    var repo = new CommandRepository();
    
    repo.Register<CreateUserParams>(CreateUser);
    repo.Register<DeleteUserParams>(DeleteUser);
    
    repo.Run(new CreateUserParams { Name = "test" });
    repo.Run(new DeleteUserParams { Name = "test" });
    
  4. 如前所述,使用Dictionary<Type, Action>中的ComandRepository可以实现相同的行为,但是如果我使用接口而不是{{1},则必须转换方法参数在获取字典项后,强制转换为Action

    我的问题是:(ab-)是否可以使用类似的泛型类型(给定IFoo<T>的大量可能值)?

    (奖金问题)如果不行,为什么不呢?这会产生什么样的成本/负面影响?

    最后一点:我意识到这不适用于类型层次结构或接口。类型T必须完全匹配,这在我的方案中很好。

    编辑:我发现{J}序列化程序Jil也依赖于此模式。请参阅TypeCache类型,该类型存储委托以序列化T类型的对象(据我所知,从浏览代码中可以理解)。由于此T将存储大量类型,因此我认为该模式通常没有问题。

    了解类型或其静态成员是否需要进行垃圾收集或是否存在需要考虑的其他性能影响仍然很有趣。

2 个答案:

答案 0 :(得分:1)

您建议的方法是可行的,但缺点是您的存储库实际上是一个单例。如果您发现自己需要一个不像单例的存储库,您可能会发现ConditionalWeakTable [1]类型很有用。使用其中一种方法的技巧是,对于每种感兴趣的类型,您将拥有一个单例ConditionalWeakTable,它将您的对象映射到与该类型相关联的事物(如果有的话)。该类仅在.NET 4.0中可用,但可以做一些很棒的事情。

[1] http://msdn.microsoft.com/en-us/library/dd287757.aspx

例如,假设有人想要一个支持OutTypeKeyedDictionarySetValue<T>(T Value)的{​​{1}}类型。可以使用静态类族bool TryGetValue<T>(out T Value),其中每个类都包含OutputMappers<T>的单例实例。 ConditionalWeakTable<OutTypeKeyedDictionary, T>实际上没有任何字段(!);相反,每个实例将纯粹用作身份令牌,用作OutTypeKeyedDictionary密钥。顺便说一下,类在ConditionalWeakTable命名空间而不是CompilerServices中的原因是它被Collections之类的东西大量使用。

答案 1 :(得分:-1)

使用C#等语言实现双重调度的常用方法是Visitor pattern,它只提供单一调度。您的示例如下所示:

接口:

interface IVisitor
{
    void VisitCreateUserParams(CreateUserParams p);

    void VisitDeleteUserParams(DeleteUserParams p);
}

interface IParams
{
    void Accept(IVisitor visitor);
}

命令参数:

class CreateUserParams : IParams
{
    public void Accept(IVisitor visitor) { visitor.VisitCreateUserParams(this); }

    public string Name { get; set; }
}

class DeleteUserParams : IParams
{
    public void Accept(IVisitor visitor) { visitor.VisitDeleteUserParams(this); }

    public string Name { get; set; }
}

命令:

class CommandHandler : IVisitor
{
    public void VisitCreateUserParams(CreateUserParams p)
    {
        Console.WriteLine("Creating " + p.Name);
    }

    public void VisitDeleteUserParams(DeleteUserParams p)
    {
        Console.WriteLine("Deleting " + p.Name);
    }
}

示例用法:

var handler = new CommandHandler();

new CreateUserParams { Name = "test" }.Accept(handler);
new DeleteUserParams { Name = "test" }.Accept(handler);