Get derived class type from a base's class sta

2019-01-11 13:53发布

问题:

i would like to get the type of the derived class from a static method of its base class.

How can this be accomplished?

Thanks!

class BaseClass {
  static void Ping () {
     Type t = this.GetType(); // should be DerivedClass, but it is not possible with a static method
  }
}
class DerivedClass : BaseClass {}

// somewhere in the code
DerivedClass.Ping();

回答1:

If I'm not mistaken, the code emitted for BaseClass.Ping() and DerivedClass.Ping() is the same, so making the method static without giving it any arguments won't work. Try passing the type as an argument or through a generic type parameter (on which you can enforce an inheritance constraint).

class BaseClass {
    static void Ping<T>() where T : BaseClass {
        Type t = typeof(T);
    }
}

You would call it like this:

BaseClass.Ping<DerivedClass>();


回答2:

This can be accomplished easily using the curiously recurring template pattern

class BaseClass<T>
    where T : BaseClass<T>
{
    static void SomeMethod() {
        var t = typeof(T);  // gets type of derived class
    }
}

class DerivedClass : BaseClass<DerivedClass> {}

call the method:

DerivedClass.SomeMethod();

This solution adds a small amount of boilerplate overhead because you have to template the base class with the derived class.

It's also restrictive if your inheritance tree has more than two levels. In this case, you will have to choose whether to pass through the template argument or impose the current type on its children with respect to calls to your static method.

And by templates I, of course, mean generics.



回答3:

A static method is defined on the type. There is no "this". You'll need to make this an instance method, instead:

class BaseClass {
    public void Ping() {
        Type t = this.GetType(); // This will work, since "this" has meaning here...
    }

You can then do:

class DerivedClass : BaseClass {}

DerivedClass instance = new DerivedClass();
instance.Ping();


回答4:

In short this is what I tried to answer here. It uses one interface to unify all derived classes and let them all call the same static method from the base class without passing a Type.

public interface IDerived
{
}
public class BaseClass<T> where T : IDerived
{
    public static void Ping()
    {
        //here you have T = the derived type
    }
}
public class DerivedClass : BaseClass<DerivedClass>, IDerived
{
    //with : BaseClass<DerivedClass> we are defining the T above
}
public class ExampleApp
{
    public void Main()
    {
        //here we can call the BaseClass's static method through DerivedClass
        //and it will know what Type calls it
        DerivedClass.Ping();    
    }
}


回答5:

Just a guess (not tested)

Type t = MethodBase.GetCurrentMethod().DeclaringType;


回答6:

It's not possible to get the derived class from a static method. As an example to illustrate why, imagine BaseClass has 2 subclasses - DerivedClass and AnotherDerivedClass - which one should be returned? Unlike polymorphic non-static methods, there is no possible association with a derived class calling a static method on a base class - the compile time type and runtime type are the same with a static method call.

You can either make the method non-static, so you then get the correct type via polymorphism, or create static method "overrides" in the subclasses, e.g.

class DerivedClass : BaseClass
{
   void Ping() { 
     BaseClass.Ping();
     // or alternatively
     BaseClass.Ping(Type.GetType("DerivedClass"));
   }
}

Your client code can then call the method in the derived class to explicitly indicate it want's the derived class version. If necessary, you might then also pass the DerivedClass type as a parameter to the base class method, to provide context that the method was called via the derived class.



回答7:

Why not just use the methods that are there already?

If you have

class BaseClass {}
partial class DerivedClass : BaseClass {}

You can look at

DerivedClass.GetType().BaseType;


回答8:

I think the following will work for this case (and several similar ones elsewhere on SO). Perf won't be too good, but if this is infrequent, that won't be a problem.

Create a stacktrace and parse it looking for a derived class. In the general case this wouldn't be too reliable or might not even work, but in specific cases, like that in the OP, I believe this will work fine. In Powershell:

$strace = (new-object diagnostics.stacktrace).tostring()
#
$frames = $strace -split "   at "
$typesFromFrames = $frames | select -skip 1| # skip blank line at the beginning
   % { ($_ -split "\(",2)[0]} |                 # Get rid of parameters string
   % {$_.substring(0,$_.lastindexof("."))} |    # Get rid of method name
   $ {$_ -as [type]}
#
# In powershell a lot of the frames on the stack have private classes
#  So $typesFromFrames.count is quite a bit smaller than $frames.count
#  For the OP, I don't think this will be a problem because:
#   1. PS isn't being used
#   2. The derived class in the OP isn't private 
#        (if it is then tweaks will be needed)
#
$derivedOnStack = $typesFromFrames | ? { $_.issubclassof( [BaseClass])}

Hopefully there will just be one element in $derivedOnStack, but it will depend on the particulars of the application. Some experimentation will be required.