Reputation: 3470
Assume we have this model :
public abstract class AbstractTableReferentielEntity {}
public class EstimationTauxReussite : AbstractTableReferentielEntity { }
I created a extension method for all classes that inherit from AbstractTableReferentielEntity.
public static EntityItemViewModel ToEntityItem<T>(this T entity)
where T : AbstractTableReferentielEntity {}
But for one specific type of AbstractTableReferentielEntity (like EstimationTauxReussite), I would like to perform a specific action, so I created a second extension method.
public static EntityItemViewModel ToEntityItem(this EstimationTauxReussite entity) {}
All extensions methods are declared in the same namespace.
After that, I retrieve some data from a DB with Entity Framework :
protected List<EntityItemViewModel> GetAllActifEntityItem<T>()
where T : AbstractTableReferentielEntity
{
return Context
.Set<T>()
.Where(item => item.IsActif)
.Select(item => item.ToEntityItem())
.ToList();
}
It compiles.
When T at runtime is a EstimationTauxReussite type, it goes into the wrong method ToEntityItem
when I call Select(item => item.ToEntityItem())
. It doesn't go into the most specific extension method. Any ideas ?
Upvotes: 6
Views: 464
Reputation: 292355
That's because extension methods are just syntactic sugar for static methods. The method to call is resolved at compile time based on the compile-time type of the argument, there is no virtual dispatch involved.
In your GetAllActifEntityItem
method, the compiler only knows that T
is a AbstractTableReferentielEntity
, so it resolves the ToEntityItem
method based on that. The fact that it will actually be called with EstimationTauxReussite
for T
is irrelevant.
A possible workaround would be to make ToEntityItem
a virtual member method of AbstractTableReferentielEntity
, and override it in EstimationTauxReussite
. This way, virtual dispatch will occur as expected and the correct method will be called.
Upvotes: 1
Reputation: 43254
The reason is that extension methods are "syntactic sugar", ie they are a compiler trick. Your line:
.Select(item => item.ToEntityItem())
is effectively converted by the compiler to:
.Select(item => StaticClassWithExtensionMethod.ToEntityItem(item))
and then turned into IL. This means that the type of item
has to be determined at compile-time, not runtime. So the AbstractTableReferentielEntity
version of the extension method is used as that's the one tht matches the type at compile time.
Upvotes: 3
Reputation: 1120
If i have access to sources of AbstractTableReferentielEntity
and EstimationTauxReussite
classes i would remake them in following way
Now Select(item => item.ToEntityItem())
should pick method depends on input object
Upvotes: 1