带接口的扩展方法

Extension methods with interface

假设我们有这个模型:

public abstract class AbstractTableReferentielEntity {}
public class EstimationTauxReussite : AbstractTableReferentielEntity { }

我为所有继承自 AbstractTableReferentielEntity 的 类 创建了一个扩展方法。

public static EntityItemViewModel ToEntityItem<T>(this T entity)
    where T : AbstractTableReferentielEntity {}

但是对于一种特定类型的 AbstractTableReferentielEntity(如 EstimationTauxReussite),我想执行特定的操作,因此我创建了第二个扩展方法。

 public static EntityItemViewModel ToEntityItem(this EstimationTauxReussite entity) {}

所有扩展方法都在同一个命名空间中声明。

之后,我使用 Entity Framework 从数据库中检索了一些数据:

protected List<EntityItemViewModel> GetAllActifEntityItem<T>()
    where T : AbstractTableReferentielEntity
{
    return Context
        .Set<T>()
        .Where(item => item.IsActif)
        .Select(item => item.ToEntityItem())
        .ToList();
}

编译通过。

当运行时的 T 是 EstimationTauxReussite 类型时,当我调用 Select(item => item.ToEntityItem()) 时它进入了错误的方法 ToEntityItem。它没有涉及最具体的扩展方法。有任何想法吗 ?

如果我可以访问 AbstractTableReferentielEntityEstimationTauxReussite classes 的来源,我会按照以下方式重新制作它们

  1. 将虚拟 ToEntityItem 方法添加到 AbstractTableReferentielEntity class
  2. 在 EstimationTauxReussite 中覆盖它 class
  3. 删除两个扩展方法

现在Select(item => item.ToEntityItem())应该选择方法取决于输入对象

原因是扩展方法是 "syntactic sugar",即它们是一个编译器技巧。您的线路:

.Select(item => item.ToEntityItem())

被编译器有效地转换为:

.Select(item => StaticClassWithExtensionMethod.ToEntityItem(item))

然后变成了IL。这意味着 item 的类型必须在编译时确定,而不是运行时。所以使用 AbstractTableReferentielEntity 版本的扩展方法,因为它与编译时的类型匹配。

那是因为扩展方法只是静态方法的语法糖。调用的方法是在编译时根据参数的编译时类型解析的,不涉及虚拟调度。

在你的GetAllActifEntityItem方法中,编译器只知道T是一个AbstractTableReferentielEntity,所以它根据那个解析ToEntityItem方法。它实际上将用 EstimationTauxReussite 调用 T 的事实无关紧要。

一种可能的解决方法是使 ToEntityItem 成为 AbstractTableReferentielEntity 的虚拟成员方法,并在 EstimationTauxReussite 中覆盖它。这样,虚拟分派将按预期发生并调用正确的方法。