简体   繁体   English

用静态方法确定引用类型

[英]Determine referenced type in static method

I think this may not be possible but if it is SO would know. 我认为这可能无法实现,但是如果可以的话。

I have a abstract class that statically maintains instances of it's subclasses. 我有一个抽象类,可以静态维护其子类的实例。 I would like to implement a static getInstance() method in my base class that will get instance of the whichever class is referenced. 我想在我的基类中实现一个静态的getInstance()方法,该方法将获取所引用的那个类的实例。 So I need a way to tell which class was referenced in the static call. 因此,我需要一种方法来告诉您在静态调用中引用了哪个类。

I think the code will make this more clear: 我认为代码将使这一点更加清楚:

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

So if I called A.getInstance() my callingType variable would be typeof(A) . 因此,如果我调用A.getInstance()我的callingType变量将为typeof(A) My primary goal is avoid having Find calls in my code to make it cleaner and more readable but I am also just curious if this is possible. 我的主要目标是避免在代码中使用Find调用,以使其更整洁,更易读,但我也很好奇是否可行。

C# does not support virtual static methods. C#不支持虚拟静态方法。

One workaround would be to use a generic method for GetInstance() . 一种解决方法是对GetInstance()使用通用方法。 Instead of calling A.GetInstance() , callers would call Base.GetInstance<A>() . 而不是调用A.GetInstance() ,调用者将调用Base.GetInstance<A>() The effect would be the same, however: 效果是一样的,但是:

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

While it would involve some boilerplate, you could then (if you choose) write convenience methods at the derived class level to obtain the same effect: 尽管这会涉及一些样板,但是您可以(如果选择)在派生类级别编写便捷方法以获得相同的效果:

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

Static methods are not inherited. 静态方法不会被继承。 They are scoped to their class. 它们仅限于他们的班级。 The way you have it written there is no "getInstance" method on A or B...only on Base. 您编写的方式在A或B上没有“ getInstance”方法...仅在Base上。 So what you're trying to do isn't quite possible via your approach. 因此,您尝试做的事情不可能完全通过您的方法来完成。

However, you can go with a generic method: 但是,您可以使用通用方法:

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

Then you can just request instances of the types like this 然后,您可以只请求这样的类型的实例

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

Just as easy as 就像

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

and because type T is limited to types that inherit base (via T : Base), you don't have to worry about other types of classes because this won't even compile: 并且由于类型T仅限于继承基类的类型(通过T:Base),因此您不必担心其他类型的类,因为它甚至不会编译:

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

Expanding on my comments above, you can't really do what you are asking without putting code in all the derived classes. 在上面的评论中,如果不将代码放入所有派生类中,您将无法真正做到所要的。 Part of the reason is that while you are able to access A.getInstance() and B.getInstance() they are both compiled down to the call Base.getInstance() . 部分原因是,尽管您能够访问A.getInstance()B.getInstance()但它们都被编译为调用Base.getInstance() See the IL of simple method such as: 请参见简单方法的IL,例如:

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

is compiled to: 编译为:

.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

Note that there is no mention of A.getInstance or B.getInstance . 请注意,这里没有提到A.getInstanceB.getInstance In fact, they do not exist in the IL. 实际上,它们在IL中不存在。 Tricks with StackFrame and such won't help because there is no method in the IL for A.getInstance() and B.getInstance() . 使用StackFrame类的技巧将无济于事,因为IL中没有A.getInstance()B.getInstance()

One workaround has been proposed by the other answers using a generic method that each derived class calls. 其他答案使用每个派生类调用的通用方法提出了一种解决方法。 This is about as simple as it is going to get. 这差不多会变得简单。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM