简体   繁体   中英

Call extension method on derived type

Introduction

I need to create an extension method on a generic abstract class, but where only the base, non-generic abstract class is ever exposed. There will never be a concrete class which inherits directly from Abstract .

Classes

public abstract class Abstract { }

public abstract class Abstract<T> : Abstract { }

public class Concrete : Abstract<string> { }

Methods

public static Abstract GetConcrete()
{
    return new Concrete();
}

public static class Extensions
{
    public static void Extension<T>(this Abstract<T> a)
    {
        Console.WriteLine("Generic");
    }
}

Usage

Abstract a = GetConcrete();
a.Extension();

Error

The above results in a compile-time error:

'Abstract' does not contain a definition for 'Extension' and no extension method 'Extension' accepting a first argument of type 'Abstract' could be found

Idea

I thought that adding an extension on Abstract but this doesn't allow me to cast to the more specific extension:

public static void Extension(this Abstract a)
{
    Console.WriteLine("Base");
    Abstract<?> cast = MagiclyCastToGeneric(a);
    Extensions.Extension(cast);
}

One way is to use the "magic" of dynamic :

public static void Extension(this Abstract a)
{
    Console.WriteLine("Base");

    dynamic d = a;

    Extensions.Extension(d);
}

This causes the overload resolution to be done at run-time rather than compile-time - the DLR will choose the most specific version of the overload to be chosen, which is Extension(this Abstract<T>) .

Beware, if there were to be a concrete type directly inheriting Abstract then the above would cause a StackOverflowException .

In case you can't use DLR/dynamic as in dav_i's answer (ie you're on .Net2.0), you can do that in a similar way, manually. Pseudocode:

public static void Extension(this Abstract a)
{
    var actualType = a.GetType();
    var interestingBaseType = ...search basetypes of actualType for Abstract<T>
    var theTypeParameter = interestingBaseType.GetGenericArguments()[0];

    var genericMethodDef = typeof(Extensions).GetMethod("Extension");
    var concreteMethod = genericMethodDef.MakeGenericMethod(theTypeParameter);

    concreteMethod.Invoke(a, ....);
}

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