带接口的扩展方法
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
。它没有涉及最具体的扩展方法。有任何想法吗 ?
如果我可以访问 AbstractTableReferentielEntity
和 EstimationTauxReussite
classes 的来源,我会按照以下方式重新制作它们
- 将虚拟 ToEntityItem 方法添加到 AbstractTableReferentielEntity class
- 在 EstimationTauxReussite 中覆盖它 class
- 删除两个扩展方法
现在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
中覆盖它。这样,虚拟分派将按预期发生并调用正确的方法。
假设我们有这个模型:
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
。它没有涉及最具体的扩展方法。有任何想法吗 ?
如果我可以访问 AbstractTableReferentielEntity
和 EstimationTauxReussite
classes 的来源,我会按照以下方式重新制作它们
- 将虚拟 ToEntityItem 方法添加到 AbstractTableReferentielEntity class
- 在 EstimationTauxReussite 中覆盖它 class
- 删除两个扩展方法
现在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
中覆盖它。这样,虚拟分派将按预期发生并调用正确的方法。