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

112

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


16
您处于静态上下文中,无法编写 typeof(ThisTypeName)?如何解决? - Bruno Reis
1
在静态方法中不存在类似于“运行时”的东西(假设您不是在谈论传递给静态方法的参数)。在这种情况下,您可以简单地说typeof(RelevantType)。 - Manish Basantani
2
一个静态方法不能是虚拟的。你已经知道了类型。 - Hans Passant
7
会有许多从抽象类派生的子类。基础抽象类具有静态字典<Int, Type>。因此,派生类在静态构造函数中“注册”自己(dic.Add(N, T))。是的,我知道Type的类型 :) 我只是有点懒,不喜欢替换文本,并想知道“T”是否可以在运行时确定。请原谅我的谎言,因为这只是为了简化问题。它起作用了 ;) 现在已经有一个被接受的解决方案了。谢谢。 - Yegor
@BrunoReis,他可能在当前方法的静态上下文中...但可能不是在调用栈的一两个帧中。但这里有一个真实的场景。WPF。您有一个控件绑定到DataContext中非静态类对象上的静态属性。现在,在您的静态属性的“get”中,您“技术上”处于静态上下文中,但作为程序员,您知道有一个真正的非静态对象,它可能从您的基类继承了静态属性...当然,这里的答案可能是不使用静态属性...但您看到了重点。 - C. Tewalt
显示剩余3条评论
8个回答

153
如果您正在寻找一个等同于静态方法 this.GetType() 的一行代码,请尝试以下内容。
Type t = MethodBase.GetCurrentMethod().DeclaringType

虽然这种方法比仅使用typeof(TheTypeName)更昂贵。

1
这一个运作良好。谢谢 :) 它不是很昂贵,因为它会被调用得相当少。 - Yegor
2
Entrase,Jared所说的“expensive”指的是对处理器来说成本高,通常意味着速度慢。但他说“much more expensive”,意思是更慢。除非你正在设计火箭制导系统,否则可能根本不会很慢。 - Dan Rosenstark
1
我曾经看到GetCurrentMethod引起了一些严重的性能问题。但是,由于你只是获取类型,所以可以将其缓存。 - Jonathan Allen
65
此方法始终返回实现当前方法的类,而不是在子类情况下调用该方法的类。 - Matt Connolly
3
如果代码被迁移到不同的类名或其他情况,避免错误可能很方便,但是一个好的重构工具应该会处理typeof(TheTypeName) - Nyerguds

60

其他答案没有完全阐明的一点是,这与您关于类型仅在执行时可用的想法相关。

如果使用派生类型来执行静态成员,则二进制中省略了实际类型名称。所以例如编译此代码:

UnicodeEncoding.GetEncoding(0);

现在用ildasm打开它...你会看到调用是这样发出的:

IL_0002:  call       class [mscorlib]System.Text.Encoding 
[mscorlib]System.Text.Encoding::GetEncoding(int32)
编译器已解决对Encoding.GetEncoding的调用-不再存在UnicodeEncoding的痕迹。恐怕这使得您对“当前类型”的想法变得毫无意义。

快进10年,为什么C#中仍然没有虚拟静态方法呢?;)通常情况下不需要它们...但有时候它们会派上用场;) - marchewek
1
@marchewek:我认为非常非常罕见 - 罕至 C# 团队始终发现更有用的事情要做。(并不是说他们在这段时间里闲着不干......) - Jon Skeet
1
我认为这是因为C#在静态类方面缺乏功能。您无法覆盖静态方法。您不能在接口中拥有静态方法或构造函数。部分可以通过反射替换。例如,序列化/反序列化。使用接口中的静态方法,您可以实现它而无需使用反射。从静态方法获取类的类型对于将单例的共享初始化代码移动到基类中非常有用。这是“我不知道我需要它”的情况,我认为。所有这些方法都在Objective-C / Swift中使用。 - Krypt
1
静态抽象在接口中很可能会在C# 11中出现(来源1,来源2)。 - JamieGL

29

另一种解决方案是使用自引用类型。

//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);
    }
}

然后在继承它的类中,我创建了一个自引用类型:

public class Child: Parent<Child>
{
}

现在,Parent类中的typeof(TSelfReferenceType)调用将获取并返回调用方的Type,而无需实例。

Child.GetType();

我已经在单例模式中使用了这个,即 Singleton<T> ... 静态成员可以在错误消息或任何需要的地方引用 typeof(T)。 - yoyo
2
嗨。我真的很喜欢并感谢这个答案,因为它给了我一个解决方案来从静态基础函数中找到子类型。 - Bill Software Engineer
1
不错。可惜在C#中,这不能做到没有一点代码重复。 - Display Name
这个解决方法还有另一个示例,请访问https://dev59.com/knHYa4cB1Zd3GeqPM5PL#22532416。 - Steven de Salas
2
这两个(包括Steven链接的那个)都不能用于基类实现者的继承者...孙子类最终会成为Child类型...太糟糕了,C#没有虚静态方法 ;) - marchewek
1
不要去看:https://ericlippert.com/2011/02/02/curiouser-and-curiouser/,即使你不在意。你应该直接使用 typeof(Child)。 - Wouter

5

在静态方法中无法使用this,因此直接这样做是不可能的。但是,如果您需要某个对象的类型,请调用GetType并将this实例作为必须传递的参数,例如:

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

这似乎是一个糟糕的设计。你确定需要在自己的静态方法中获取实例本身的类型吗?这似乎有点奇怪。为什么不使用实例方法呢?
public class Car {
  public void Drive() { // Remove parameter; doesn't need to be static.
    Console.WriteLine("Driving a {0}", this.GetType());
  }
}

3

我不明白为什么你不能使用typeof(ThisTypeName)。如果这是一个非泛型类型,那么应该可以使用:

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

如果它是一个泛型类型,那么:
class Foo<T> {
    static void Method1 () {
       Type t = typeof (Foo<T>);
    }
}

我有没有漏掉什么显而易见的东西?


8
如果你创建了一个从Foo继承的类Bar,并且该类继承Method1,那么对Bar.Method1的调用仍将处理typeof(Foo),这是错误的。继承的Method1应该以某种方式知道它是在Bar中派生的,然后获取typeof(Bar)。 - JustAMartin

0

对于我的目的,我喜欢@T-moty的想法。尽管我多年来一直使用“自引用类型”信息,但稍后引用基类更难。

例如(使用@Rob Leclerc上面的例子):

public class ChildA: Parent<ChildA>
{
}

public class ChildB: Parent<ChildB>
{
}

使用这种模式可能会有一些挑战,例如:如何从函数调用中返回基类?
public Parent<???> GetParent() {}

或者类型转换时呢?

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

所以,我尽可能避免使用它,并在必要时使用它。如果你必须使用它,我建议你遵循这个模式:

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) {}
}

现在你可以更轻松地使用BaseClass了。然而,有时候,比如我的当前情况,从基类中公开派生类并不是必要的,使用@M-moty的建议可能是正确的方法。

然而,只有当基类在调用堆栈中不包含任何实例构造函数时,才能使用@M-moty的代码。不幸的是,我的基类确实使用实例构造函数。

因此,这里是我的扩展方法,考虑到基类的“实例”构造函数:

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;
    }
}

0

当您的成员是静态的时,您总是可以在运行时知道它属于哪种类型。在这种情况下:

class A
{
  public static int GetInt(){}

}
class B : A {}

你不能调用(编辑:显然,你可以,见下面的评论,但你仍然会调用到A):

B.GetInt();

由于该成员是静态的,因此在继承情况下不起作用。因此,您始终知道类型为A。


4
你可以调用B.GetInt()方法 - 至少在它被声明为私有之前是可以的 - 但编译器会将其转换为对A.GetInt()方法的调用。试试看吧! - Jon Skeet
关于Jon的评论:C#中的默认可见性是private,因此你的例子不起作用。 - Dan Rosenstark

0

编辑 这些方法仅在您将PDB文件与可执行文件/库一起部署时才有效,正如markmnl向我指出的那样。

否则,检测到问题可能会是一个巨大的问题:在开发中运行良好,但在生产中可能不行。


实用方法,只需在代码的任何位置调用该方法:

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;
}

1
StackTrace 只在 Debug 构建中可用。 - markmnl
只有在发布模式下部署.pdb文件时,才能使用StackTrace。https://dev59.com/4kzSa4cB1Zd3GeqPkz6H - T-moty
我明白你的意思。一个方法只有在部署了PDB文件时才能工作是不可接受的。我会编辑答案。 - T-moty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接