【问题标题】:Calling a generic method with the correct derived type使用正确的派生类型调用泛型方法
【发布时间】:2012-05-31 09:31:19
【问题描述】:

我有以下场景:

我有三个班级,我们称它们为ABC。它们的共同点是它们继承自同一个接口ISomeInterface,并且它们是使用实体框架映射到实体的类。

我有一个方法接收实现此接口的对象列表,但对象本身将是 ABC 的实例。

方法外壳是这样的

public void MyMethod(List<ISomeInterface> entityList)
{
  foreach(var entity in entityList)
  {
    ProcessEntity(entity);
  }
}

现在,问题出在ProcessEntity 方法上。这是一个通用方法,需要根据类型或实体从数据库中检索匹配元素的表,所以它看起来像这样:

public void ProcessEntity<T>(T entity)
{
  using( var repository = new DbRepository())
  {
    var set = repository.Set<T>();
    ...
  }
}

问题在于var set = repository.Set&lt;T&gt;(); 行失败,因为在这种情况下TISomeInterface,而不是实际类型(ABC),所以它给出了一个异常is 不能与给定的类型相关,这是可以理解的。

所以我的问题是:如何使用列表中对象的实际类型而不是它们实现的接口类型来调用 ProcessEntity。

【问题讨论】:

    标签: c# generics inheritance


    【解决方案1】:

    您可以在将实体传递给 ProcessEntity 时应用 dynamic 关键字。在这种情况下,实体的实际类型将在运行时确定。

    public void MyMethod(List<ISomeInterface> entityList)
    {
      foreach(var entity in entityList)
      {
        dynamic obj = entity;
        ProcessEntity(obj);
      }
    }
    

    【讨论】:

    • 成功了。我将ProcessEntity(obj); 更改为ProcessEntity(obj as dynamic);,效果很好。我不知道的动态用途。非常感谢:)
    • @ØyvindKnobloch-Bråthen 是的,我非常喜欢这种运行时类型化
    【解决方案2】:

    好吧,您可以使用类似访问者的技巧并使用以下解决方法:

    1. ISomeInterface 中定义一个方法Process(EntityProcessor ep)
    2. A 中实现它,就像在ep.ProcessEntity&lt;A&gt;(this) 中一样(在BC 中也是如此)
    3. 在你的循环中不用ProcessEntity(entity),只需调用entity.Process(this)

    (方法名称可能不是最干净的,但你应该明白)

    【讨论】:

    • 这可行,但lazyberezovsky 的回答对我来说更清晰,因为这意味着我不必在 A、B 和 C 中添加更多逻辑(实际上有 20 种这样的类型,而不是3 ;) )
    • @Øyvind:嗯,我的解决方案需要更多的工作,但它会在编译时检查所需方法的存在。同样,它可能会稍微快一些。
    • 我明白你的意思,但是使用动态你根本没有需要的方法,我希望 A、B 和 C 类也没有关于这个功能的任何知识.这也意味着我不必记住为应该遵循相同模式的新类添加这个额外的功能。
    • @Øyvind:dynamic 的使用使代码更紧凑,但代价是牺牲了一些类型安全。与软件开发一样,这是一种权衡。对于新类,您不会忘记实现所需的成员,因为它是接口所必需的。
    • 完全同意。就像大多数其他事情一样,这是一种权衡。不确定界面成员指的是什么。我最终得到的实现不使用接口的任何成员,它只是检查类是否实现了接口,但实际的接口实现用于其他用途,而不是用于此特定功能。
    【解决方案3】:

    您可以使用反射来获取泛型方法定义,然后调用它,例如:

    var method = typeof(ClassContainingProcessEntity)
        .GetMethod(ProcessEntity)
        .MakeGenericMethod(entity.GetType);
    method.Invoke(this, entity);
    

    您可以按类型缓存方法,如果性能很关键,您可以在运行时使用某种委托工厂对其进行编译。

    您也可以使用Visitor pattern

    【讨论】:

    • 是的,两者都应该工作,如果我在这里没有更好的答案,我正在考虑走反思路线。似乎动态修复了这个更清洁。感谢您的意见:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-24
    • 2019-03-08
    • 2012-04-25
    • 2017-07-14
    • 2023-02-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多