C# inheritance - save reference to last instance

2019-09-16 11:24发布

问题:

I just came up with a really odd problem and I wasn't able to figure out how to solve it.

I have 3 classes, the class A is the base for B and C, that is:

class A { ... }
class B : A { ... }
class C : B { ... }

Now I would like to have a static property in these classes that stores the last object of each classes created, for example:

class A
{
    static public A lastInstance;
}

class B : A
{
    public B()
    {
        lastInstance = this;
    }
}

class C : A
{
    public C()
    {
        lastInstance = this;
    }
}

What I would like to achieve is to be able to retrieve an instance for each subclass, for example:

var v1 = new B();
var v2 = new C();

var v3 = B.lastInstance;    // v3 == v1 and v3 != v2
var v4 = C.lastInstance;    // v4 == v2 and v4 != v3

Is it possible anyhow?

The only approach that seems promising to me shown in C# Static instance members for each inherited class: is it really the only chance I have to avoid defining a static member manually for each class?

回答1:

I think this could be done with Dictionary and that's the only way i can think of right now:

class A {
    static Dictionary<Type, A> _LastInstances = new Dictionary<Type, A>(); // because every subclass will inherit from A
    public static A LastInstance {
        get {
            if ( _LastInstances.ContainsKey(GetType()) ) {
                return _LastInstances[GetType()];
            }
            return null;
        }

        protected set {
            if ( _LastInstances.ContainsKey(GetType()) ) {
                _LastInstances[GetType()] = value;
            } else {
                _LastInstances.Add(GetType(), value);
            }
        }
}

class B : A {
    public B(){
        LastInstance = this;
    }
}


回答2:

At first: yes you can. But you missed two points with your implementation.

  1. As you declared the lastInstance as public in class A every derived class can use it. As you declared it as static every instance of A will copy itself into it. But so will every instance of B, C and every other class with id derived from A: they all use the same instance. Thus the last instatiated class is saved and everything instantiated before is overwritten.

To overcome this, you must have a static property LastInstance (I switched to my naming convention) on every class, which you can accomplish by using the new modifier on derived classes

public class A
{
   public static A LastInstance { get; private set; }
   ...
}

public class B : A
{
   public static new B LastInstance { get; private set; }
   ...
}

But you're not done with that alone, because

  1. When you create a new instance of B the (default-)construstor first makes a call into the construtor of A. Thus a reference to any already created instance of a base class is overwritten by the currently created instance of the derived class. So your constructors should look like this:
public class A
{
   public static A LastInstance { get; private set; }
   public A()
   {
      if (this.GetType() == typeof(A))
      {
         LastInstance = this;
      }
   }
}

public class B : A
{
   public static new B LastInstance { get; private set; }
   public B()
   {
      if (this.GetType() == typeof(B))
      {
         LastInstance = this;
      }
   }
}

This way you will get the correct lastly created instance (if any) in each classes static LastInstance.

Hope this helps



回答3:

Because static members aren't inherited, you won't be able to access B.lastInstance if class A defines lastInstance. The suggestion you linked to seems reasonable. Although I don't have enough information on why you're attempting this, you could consider using a factory class that holds onto the latest created object.

Here's an example. This is not a good long term solution if you plan to have many classes deriving from A.

class HoldLastKnownFactory
{
    B CreateB() { ... }
    C CreateC() { ... }
    B LastB { get {...} }
    C LastC { get {...} }
}