【问题标题】:.NET: Determine the type of “this” class in its static method.NET:在其静态方法中确定“this”类的类型
【发布时间】:2011-01-06 02:23:49
【问题描述】:

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

【问题讨论】:

  • 您处于静态上下文中,无法编写 typeof(ThisTypeName)?怎么样?
  • 在静态方法中没有像“运行时”这样的东西(假设您不是在谈论传递给静态方法的参数)。在这种情况下,您可以简单地说 typeof(RelevantType) 。
  • 静态方法不能是虚拟的。你已经知道类型了。
  • 从抽象派生类会有很多。基本抽象类具有静态字典。因此派生类在静态构造函数中“注册”自身(dic.Add(N, T))。是的,我确实知道类型:) 我只是有点懒,不喜欢替换文本,想知道是否可以在运行时确定“T”。请原谅我的谎言,因为它只是为了简化问题。它起作用了;)现在有一个公认的解决方案。谢谢。
  • 子类继承其父类的静态方法,不是吗?超类的静态方法对它的所有子类都有用不是很有意义吗?静态就是没有实例的意思,公用基类中公用代码的原则,对静态方法和实例方法一样适用吗?

标签: c# .net types static-methods


【解决方案1】:

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

//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 ...静态成员可以在错误消息或其他任何需要的地方引用 typeof(T)。
  • 嗨。我真的很喜欢并欣赏这个答案,因为它让我可以从静态基函数中找到子类型。
  • 不错的一个。太遗憾了,在 C# 中,如果没有这个小代码重复,这将无法完成。
  • stackoverflow.com/a/22532416/448568上还有另一个解决方法的示例
  • 两者(这个和由 Steven 链接的那个)都不适用于基类实现者的继承者......孙子最终会成为 Child 类型......太糟糕的 C# 没有t 有虚拟静态 ;)
【解决方案2】:

有些东西其他答案还没有完全阐明,这与您认为类型仅在执行时可用的想法有关。

如果使用派生类型来执行静态成员,real 类型名称在二进制文件中被省略。例如,编译这段代码:

UnicodeEncoding.GetEncoding(0);

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

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

编译器已解析对Encoding.GetEncoding 的调用 - 没有留下任何UnicodeEncoding 的痕迹。恐怕这会让你对“当前类型”的想法变得荒谬。

【讨论】:

  • 快进 10 年,为什么 C# 中还没有虚拟静态? ;) 通常一个人不需要它们......但在极少数情况下它们会派上用场;)
  • @marchewek:我会说非常非常罕见 - 非常罕见,以至于 C# 团队总能找到更有用的事情去做。 (他们不是一直闲着……)
  • 我认为是因为 C# 通常缺少静态类的功能。您不能覆盖静态方法。接口中不能有静态方法或构造函数。部分可以用反射代替。示例案例是反序列化。使用接口中的静态方法等功能,您可以在没有反射的情况下实现它。从静态方法获取类的类型对于将单例的共享初始化代码移动到基类中很有用。我确实认为这是“我不知道我需要它”的情况。所有这些方法都在 Objective-C/Swift 中使用。
  • 接口中的静态抽象可能会出现在 C# 11 中(source 1source 2
【解决方案3】:

编辑 正如markmnl 向我指出的那样,此方法仅在您使用可执行文件/库部署 PDB 文件时才有效。

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


实用方法,只需在需要时从代码的每个位置调用该方法:

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

【讨论】:

  • StackTrace 仅在调试版本中可用
  • 不正确:当您还在发布模式下部署 .pdb 文件时,可以使用 StackTrace。 stackoverflow.com/questions/2345957/…
  • 我明白你的意思。一种方法仅在部署 PDB 文件时才有效是不可接受的。我会编辑答案
【解决方案4】:

出于我的目的,我喜欢@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;
    }
}

【讨论】:

    【解决方案5】:

    当您的成员是静态的时,您将始终知道它在运行时属于什么类型。在这种情况下:

    class A
    {
      public static int GetInt(){}
    
    }
    class B : A {}
    

    你不能打电话(编辑:显然,你可以,见下面的评论,但你仍然会打电话给 A):

    B.GetInt();
    

    因为成员是静态的,所以它不参与继承场景。所以,你总是知道类型是 A。

    【讨论】:

    • 可以调用 B.GetInt() - 至少,如果它不是私有的,你可以 - 但编译会将其转换为对 A.GetInt() 的调用)。试试看!
    • 请注意 Jon 的评论:C# 中的默认可见性是私有的,因此您的示例不起作用。
    【解决方案6】:

    我不明白为什么你不能使用 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>);
        }
    }
    

    我在这里遗漏了什么明显的东西吗?

    【讨论】:

    • 如果您创建一个从 Foo 派生的 Bar 类,然后该类继承 Method1,这将不起作用 - 然后对 Bar.Method1 的调用仍将处理 typeof(Foo) 这是错误的。继承的 Method1 应该不知怎么知道它是在 Bar 中派生的,然后得到 typeof(Bar)。
    【解决方案7】:

    如果您正在寻找与 this.GetType() 等效的静态方法的 1 行,请尝试以下方法。

    Type t = MethodBase.GetCurrentMethod().DeclaringType
    

    尽管这可能比仅使用 typeof(TheTypeName) 贵得多。

    【讨论】:

    • 这个工作正常。谢谢 :) 它并没有那么贵,因为它会被称为非常稀有。
    • Entrase,“昂贵” Jared 表示它们对处理器来说成本很高,通常意味着速度很慢。但他说,“更贵”意味着更慢。可能一点也不慢,除非你正在设计一个火箭制导系统。
    • 我看到 GetCurrentMethod 会导致一些严重的性能问题。但是由于您只是获取类型,因此可以对其进行缓存。
    • 这总是返回实现当前方法的类,而不是在子类的情况下调用它的类。
    • 我想如果代码被迁移到不同的类名或其他东西,避免错误很方便,但是一个好的重构工具应该照顾typeof(TheTypeName)
    【解决方案8】:

    您不能在静态方法中使用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());
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-07
      • 1970-01-01
      • 2021-03-17
      • 2010-09-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多