.NET: 在其静态方法中确定“ this”类的类型

在非静态方法中,我可以使用 this.GetType(),它将返回 Type。如何在静态方法中获得相同的 Type?当然,我不能只编写 typeof(ThisTypeName),因为 ThisTypeName只在运行时才知道。谢谢!

77048 次浏览

You can't use this in a static method, so that's not possible directly. However, if you need the type of some object, just call GetType on it and make the this instance a parameter that you have to pass, e.g.:

public class Car {
public static void Drive(Car c) {
Console.WriteLine("Driving a {0}", c.GetType());
}
}

This seems like a poor design, though. Are you sure that you really need to get the type of the instance itself inside of its own static method? That seems a little bizarre. Why not just use an instance method?

public class Car {
public void Drive() { // Remove parameter; doesn't need to be static.
Console.WriteLine("Driving a {0}", this.GetType());
}
}

If you're looking for a 1 liner that is equivalent to this.GetType() for static methods, try the following.

Type t = MethodBase.GetCurrentMethod().DeclaringType

Although this is likely much more expensive than just using typeof(TheTypeName).

When your member is static, you will always know what type it is part of at runtime. In this case:

class A
{
public static int GetInt(){}


}
class B : A {}

You cannot call (edit: apparently, you can, see comment below, but you would still be calling into A):

B.GetInt();

because the member is static, it does not play part in inheritance scenarios. Ergo, you always know that the type is A.

I don't understand why you cannot use typeof(ThisTypeName). If this is a non-generic type, then this should work:

class Foo {
static void Method1 () {
Type t = typeof (Foo); // Can just hard code this
}
}

If it's a generic type, then:

class Foo<T> {
static void Method1 () {
Type t = typeof (Foo<T>);
}
}

Am I missing something obvious here?

There's something that the other answers haven't quite clarified, and which is relevant to your idea of the type only being available at execution time.

If you use a derived type to execute a static member, the real type name is omitted in the binary. So for example, compile this code:

UnicodeEncoding.GetEncoding(0);

Now use ildasm on it... you'll see that the call is emitted like this:

IL_0002:  call       class [mscorlib]System.Text.Encoding
[mscorlib]System.Text.Encoding::GetEncoding(int32)

The compiler has resolved the call to Encoding.GetEncoding - there's no trace of UnicodeEncoding left. That makes your idea of "the current type" nonsensical, I'm afraid.

Another solution is to use a selfreferecing type

//My base class
//I add a type to my base class use that in the
//static method to check the type of the caller.
public class Parent<TSelfReferenceType>
{
public static Type GetType()
{
return typeof(TSelfReferenceType);
}
}

Then in the class that inherits it, I make a self referencing type:

public class Child: Parent<Child>
{
}

Now the call type typeof(TSelfReferenceType) inside Parent will get and return the Type of the caller without the need of an instance.

Child.GetType();

EDIT This methods will works only when you deploy PDB files with the executable/library, as markmnl pointed out to me.

Otherwise will be a huge issue to be detected: works well in developement, but maybe not in production.


Utility method, simply call the method when you need, from every place of your code:

public static Type GetType()
{
var stack = new System.Diagnostics.StackTrace();


if (stack.FrameCount < 2)
return null;


return (stack.GetFrame(1).GetMethod() as System.Reflection.MethodInfo).DeclaringType;
}

For my purposes, I like @T-moty's idea. Even though I have used "self-referencing type" information for years, referencing the base class is harder to do later.

For example (using @Rob Leclerc example from above):

public class ChildA: Parent<ChildA>
{
}


public class ChildB: Parent<ChildB>
{
}

Working with this pattern can be challenging, for example; how do you return the base class from a function call?

public Parent<???> GetParent() {}

Or when type casting?

var c = (Parent<???>) GetSomeParent();

So, I try to avoid it when I can, and use it when I must. If you must, I would suggest that you follow this pattern:

class BaseClass
{
// All non-derived class methods goes here...


// For example:
public int Id { get; private set; }
public string Name { get; private set; }
public void Run() {}
}


class BaseClass<TSelfReferenceType> : BaseClass
{
// All derived class methods goes here...


// For example:
public TSelfReferenceType Foo() {}
public void Bar(TSelfRefenceType obj) {}
}

Now you can (more) easily work with the BaseClass. However, there are times, like my current situation, where exposing the derived class, from within the base class, isn't needed and using @M-moty's suggestion just might be the right approach.

However, using @M-moty's code only works as long as the base class doesn't contain any instance constructors in the call stack. Unfortunately my base classes do use instance constructors.

Therefore, here's my extension method that take into account base class 'instance' constructors:

public static class TypeExtensions
{
public static Type GetDrivedType(this Type type, int maxSearchDepth = 10)
{
if (maxSearchDepth < 0)
throw new ArgumentOutOfRangeException(nameof(maxSearchDepth), "Must be greater than 0.");


const int skipFrames = 2;  // Skip the call to self, skip the call to the static Ctor.
var stack = new StackTrace();
var maxCount = Math.Min(maxSearchDepth + skipFrames + 1, stack.FrameCount);
var frame = skipFrames;


// Skip all the base class 'instance' ctor calls.
//
while (frame < maxCount)
{
var method = stack.GetFrame(frame).GetMethod();
var declaringType = method.DeclaringType;


if (type.IsAssignableFrom(declaringType))
return declaringType;


frame++;
}


return null;
}
}