【问题标题】:Determine referenced type in static method确定静态方法中的引用类型
【发布时间】:2014-05-27 03:14:50
【问题描述】:

我认为这可能是不可能的,但如果是这样就会知道。

我有一个静态维护其子类实例的抽象类。我想在我的基类中实现一个静态getInstance() 方法,该方法将获取引用的任何类的实例。所以我需要一种方法来判断静态调用中引用了哪个类。

我认为代码会更清楚地说明这一点:

abstract class Base
{
    private static List<Base> allInstances;
    public static List<Base> AllInstances
    {
        get {
            if(allInstances==null)
            {
                // Implementation not relevant and not included to avoid clutter
            }
            return allInstances;
        }
    }
    public static Base getInstance()
    {
        Type callingType = // This is what I am trying to fill in
        if(callingType == typeof(Base))
            throw new InvalidOperationException("Cannot get instance of Base class");
        return AllInstances.Find(i => i.GetType() == callingType);
    }
}


class A:Base { }
class B:Base { }

所以如果我调用A.getInstance() 我的callingType 变量将是typeof(A)。我的主要目标是避免在我的代码中调用 Find 以使其更清晰、更具可读性,但我也很好奇这是否可能。

【问题讨论】:

  • 你的意思是getInstance 是静态的吗?您正在谈论它,就像它是静态的一样。如果没有,请使用this.GetType()
  • @mikez 我做到了。我重写了这个以显示我在说什么,而不是使用我的实际代码,并且没有足够仔细地校对它。我已经修好了。
  • 我明白了。您不能不将某些内容放入所有派生类中。这是因为虽然您可以使用A.getInstanceB.getInstance 调用该方法,但它会编译为Base.getInstance
  • @mikez 我怀疑这可能是编译器会如何处理它,但不确定。如果您将其发布为答案,我会标记它。
  • 您可以做一个非常骇人听闻的技巧并使用callingType = new StackTrace().GetFrame(1).GetMethod().ReflectedType; 这将获取调用者的堆栈帧,然后提取调用类型。另见msdn.microsoft.com/en-us/library/…

标签: c# c#-2.0


【解决方案1】:

扩展我上面的 cmets,如果不将代码放入所有派生类中,您将无法真正做到您所要求的。部分原因是虽然您可以访问A.getInstance()B.getInstance(),但它们都被编译为调用Base.getInstance()。参见简单方法的IL如:

public static void CallGetInstance()
{
    var a = A.getInstance();
    var b = B.getInstance();
    Console.WriteLine(a == b);
}

编译为:

.method private hidebysig static
    void CallGetInstance () cil managed
{
    // Method begins at RVA 0x2054
    // Code size 24 (0x18)
    .maxstack 2
    .locals init (
        [0] class TestIL.Base a,
        [1] class TestIL.Base b
    )

    IL_0000: nop
    IL_0001: call class TestIL.Base TestIL.Base::getInstance()
    IL_0006: stloc.0
    IL_0007: call class TestIL.Base TestIL.Base::getInstance()
    IL_000c: stloc.1
    IL_000d: ldloc.0
    IL_000e: ldloc.1
    IL_000f: ceq
    IL_0011: call void [mscorlib]System.Console::WriteLine(bool)
    IL_0016: nop
    IL_0017: ret
} // end of method Program::CallGetInstance

请注意,这里没有提到 A.getInstanceB.getInstance。事实上,它们在 IL 中并不存在。使用StackFrame 之类的技巧无济于事,因为IL 中没有A.getInstance()B.getInstance() 的方法。

其他答案使用每个派生类调用的通用方法提出了一种解决方法。这很简单。

【讨论】:

    【解决方案2】:

    C# 不支持虚拟静态方法。

    一种解决方法是对GetInstance() 使用通用方法。调用者不会调用A.GetInstance(),而是调用Base.GetInstance&lt;A&gt;()。但是效果是一样的:

    abstract class Base
    {
    ...
      public static T getInstance<T>() where T : Base
      {
        Type callingType = typeof(T);
        // no need to check type; T will always be a type of or derived from Base
        return AllInstances.Find(i => i.GetType() == callingType) as T;
      }
    }
    

    虽然它会涉及一些样板,但您可以(如果您选择)在派生类级别编写便利方法以获得相同的效果:

    class A:Base 
    {
        public static A getInstance() {return Base.getInstance<A>();}
    }
    class B:Base
    {
        public static B getInstance() {return Base.getInstance<B>();}
    }
    

    【讨论】:

      【解决方案3】:

      静态方法不会被继承。他们的范围仅限于他们的班级。您编写它的方式在 A 或 B 上没有“getInstance”方法......仅在 Base 上。因此,通过您的方法,您尝试做的事情不太可能。

      但是,您可以使用通用方法:

      public static T GetInstance<T>() where T : Base {
          return (T)AllInstances.Find(i => i.GetType() == typeof(T));
      }
      

      然后你可以请求这样的类型的实例

      var a = Base.GetInstance<A>();
      var b = Base.GetInstance<B>();
      

      就这么简单

      var a = A.getInstance();
      var b = B.getInstance();
      

      并且因为类型 T 仅限于继承基类的类型(通过 T : Base),所以您不必担心其他类型的类,因为这甚至不会编译:

      var x = Base.GetInstance<SomeOtherRandomClass>();
      

      【讨论】:

      • 通用路线是我已经做过的,但我觉得我的问题的实际答案是“A.getInstance()Base.getInstance() 编译相同”,这就是我想让 mike z 发布的原因作为答案。这个解决方案是一个很好的解决方法,而不是所问问题的答案。
      猜你喜欢
      • 2011-01-06
      • 1970-01-01
      • 1970-01-01
      • 2013-08-02
      • 1970-01-01
      • 2020-11-19
      • 1970-01-01
      • 2012-02-10
      • 2010-09-22
      相关资源
      最近更新 更多