13

Without any code in the subclasses, I'd like an abstract class to have a different copy of a static variable for each subclass. In C#

abstract class ClassA
{
    static string theValue;

    // just to demonstrate
    public string GetValue()
    {
        return theValue;
    }
    ...
}
class ClassB : ClassA { }
class ClassC : ClassA { }

and (for example):

(new ClassB()).GetValue(); // returns "Banana"
(new ClassC()).GetValue(); // returns "Coconut"

My current solution is this:

abstract class ClassA
{
    static Dictionary<Type, string> theValue;
    public string GetValue()
    {
        return theValue[this.GetType()];
    }
    ...
}

While this works fine, I'm wondering if there's a more elegant or built-in way of doing this?

This is similar to Can I have different copies of a static variable for each different type of inheriting class, but I have no control over the subclasses

Community
  • 1
  • 1
ste
  • 818
  • 2
  • 9
  • 16

6 Answers6

17

There is a more elegant way. You can exploit the fact that statics in a generic base class are different for each derived class of a different type

public abstract class BaseClass<T> where T : class
{
    public static int x = 6;
    public int MyProperty { get => x; set => x = value; }
}

For each child class, the static int x will be unique for each unique T Lets derive two child classes, and we use the name of the child class as the generic T in the base class.

public class ChildA: BaseClass<ChildA>
{
}

public class ChildB : BaseClass<ChildB>
{
}

Now the static MyProperty is unique for both ChildA and ChildB

var TA = new ChildA();
TA.MyProperty = 8;
var TB = new ChildB();
TB.MyProperty = 4;
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
JimbobTheSailor
  • 1,441
  • 1
  • 12
  • 21
  • No, this is just wrong. TA and TB MyProperty would both be 4 now. – TheSoftwareJedi May 23 '18 at 19:59
  • 1
    @TheSoftwareJedi. Wrong? Not so my friend, It appears you don't understand the implementation of statics in abstract generic classes. Can you please create a quick console app, copy my above code, and then run it. Then please retract your comment, your down vote, and perhaps consider up voting, as it is the most elegant answer to question posed. – JimbobTheSailor May 24 '18 at 23:28
  • 1
    You are correct and I apologize. I was overlooking the generics!! I made a minor edit ot your answer merely so I could vote on it. – TheSoftwareJedi May 25 '18 at 12:39
  • However, the OP can't use it because "I have no control over the subclasses" – TheSoftwareJedi May 25 '18 at 12:41
  • 1
    Yes, this little 'feature' of statics in abstract generics cost me over a day of debugging an app. i was really surprised that the statics are unique in the child classes. It was certainly not what I even wildly expected – JimbobTheSailor May 26 '18 at 00:14
  • Perfect. That ends the religion wars on statics :D . – Volkan Mar 28 '19 at 13:54
7

While this works fine, I'm wondering if there's a more elegant or built-in way of doing this?

There isn't really a built-in way of doing this, as you're kind of violating basic OO principles here. Your base class should have no knowledge of subclasses in traditional object oriented theory.

That being said, if you must do this, your implementation is probably about as good as you're going to get, unless you can add some other info to the subclasses directly. If you need to control this, and you can't change subclasses, this will probably be your best approach.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
2

This is a little different than what you're asking for, but perhaps accomplishes the same thing.

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine((new B()).theValue);
            Console.WriteLine((new C()).theValue);
            Console.ReadKey();
        }
    }

    public abstract class A
    {
        public readonly string theValue;

        protected A(string s)
        {
            theValue = s;
        }
    }

    public class B : A
    {
        public B(): base("Banana")
        {
        }
    }

    public class C : A
    {
        public C(): base("Coconut")
        {
        }
    }
Pavel Anikhouski
  • 21,776
  • 12
  • 51
  • 66
Matthew Sposato
  • 1,635
  • 1
  • 11
  • 13
0

What about this?



    class Base {
    protected static SomeObjectType myVariable;

    protected void doSomething()
    {
    Console.WriteLine( myVariable.SomeProperty );
    }
    }

    class AAA : Base
    {
    static AAA()
    {
    myVariable = new SomeObjectType();
    myVariable.SomeProperty = "A";
    }
    }

    class BBB : Base
    {
    static BBB()
    {
    myVariable = new SomeObjectType();
    myVariable.SomeProperty = "B";
    }
    }

It works for me. Would be even nicer with Interface.

Gulyo
  • 25
  • 1
  • Care to explain what is the code doing for benefit of others, a code dump with no explanation will not satisfy SO users :) – t0mm13b Dec 12 '12 at 00:25
  • 3
    No, this would never work. Depending on when the VM sees the types, thats when the static constructor would be called, and hence 'myVariable' will contain the last-ran-static-constructor's value. And even worse, it'll be indeterminate who that last constructor will be. – enorl76 Feb 26 '13 at 04:06
0

There's an alternative solution which might or might not be better than yours, depending on the use case:

abstract class ClassA
{
    private static class InternalClass<T> {
        public static string Value;
    }
    public string GetValue()
    {
        return (string)typeof(InternalClass<>)
              .MakeGenericType(GetType())
              .GetField("Value", BindingFlags.Public | BindingFlags.Static)
              .GetValue(null);
    }
}

This approach is used in EqualityComparer<T>.Default. Of course, it's not used for this problem. You should really consider making GetValue abstract and override it in each derived class.

Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • @enorl76 This question is very old so I don't recall the actual context, but I don't think this approach requires overriding GetValue. The final sentence of my answer is just a comment on the right approach, not part of the solution. – Mehrdad Afshari Feb 26 '13 at 05:54
-1

Simple solution: just use word "new".

public abstract class AbstractClass
{
    public static int Variable;
}

public class RealizationA : AbstractClass
{
    public new static int Variable;
}

public class RealizationB : AbstractClass
{
    public new static int Variable;
}

And the result:

AbstractClass.Variable = 1;
RealizationA.Variable = 2;
RealizationB.Variable = 3;
Console.WriteLine(AbstractClass.Variable); //1
Console.WriteLine(RealizationA.Variable); //2
Console.WriteLine(RealizationB.Variable); //3

or you can use property:

//in abstract class
public static int Variable {get; set;}
//in child class
public static new int Variable {get; set;}

or function (but remember to add "new" to both variable and function):

//in abstract class
protected static int Variable;
public static int GetVariable() { return Variable; }
public static void SetVariable(int v) { Variable = v; }
//in child class
protected new static int Variable;
public static new int GetVariable() { return Variable; }
public static new void SetVariable(int v) { Variable = v; }

or you can use private variables (you don't need to use "new") with functions to get and set:

//in abstract class
private static int Variable;
//get and set methods
//in child class
private static int Variable;
//get and set methods
Alexander
  • 34
  • 4