简体   繁体   中英

Extension methods with interface

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 ?

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.

If i have access to sources of AbstractTableReferentielEntity and EstimationTauxReussite classes i would remake them in following way

  1. Add virtual ToEntityItem method to AbstractTableReferentielEntity class
  2. Override it in EstimationTauxReussite class
  3. Delete both extension methods

Now Select(item => item.ToEntityItem()) should pick method depends on input object

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM