leAthlon
leAthlon

Reputation: 744

Inheritance of something like a static field

this question is a bit more theoretical, it's not a problem which is part of homework or an important project... I'm asking this question because I think this could be quite handy to know, but I couldn't find an approtiate answer.

Problem description:

.

abstract class A
{
    public static abstract Image Symbol{get;}
}
class B:A
{
  //this Field should be forced for every class that inherits from A
  public static override Image Symbol{get{return....}}
}

of course, I could have used instead of static the readonly keyword.

But this would mean that every instance of class B would cause an extra image, because readonly allows fields to be set during runtime in the constructor ...leading to memory waste... maybe not a huge memory waste because there would be a reference waste more or less, but it's still wasting stuff... [Correct me if I'm wrong.]

So... how is this easy and nice possible by using inheritance rules?

Upvotes: 2

Views: 5110

Answers (4)

ecn
ecn

Reputation: 1

I know it's an old question, but i just playing around with static fields and inheritance and want to share my results. In my current project i got a complex data structure in semiconductor context... So my examples wont fit to the question but could properly demonstrate how to realize some kind of static field inheritance.

so here are my workarounds:

First workaround (using generics):

public abstract class TESTAbstract 
{
    //overall list of elements
    public static List<TESTAbstract> Elements = new List<TESTAbstract>();
    public static int Count { get; private set; }
    public int localId { get; protected set; }
    public int globalId { get; protected set; }

    //members here
    public int someValue = 0;

    public TESTAbstract()
    {
        globalId = Count; Count++;
        Elements.Add(this);
    }
}



public abstract class TESTAbstract<T> : TESTAbstract where T : TESTAbstract
{
    new public static List<T> Elements = new List<T>();
    new public static int Count { get; private set; }

    public TESTAbstract()
        : base()
    {
        localId = Count; Count++;
        Elements.Add(this as T);
    }
}

The implementation looks like:

public class TEST1 : TESTAbstract<TEST1>
{
    public TEST1(int i)
        : base()
    {
        this.someValue = i;
    }

}

public class TEST2 : TESTAbstract<TEST2>
{
    public TEST2(int i)
        : base()
    {
        this.someValue = i;
    }

}

Usage:

            TESTAbstract test1 = new TEST1(1001);
            TESTAbstract test2 = new TEST2(1002);
            TESTAbstract test3 = new TEST2(1003);
            TESTAbstract test4 = new TEST1(1004);
            TESTAbstract test5 = new TEST1(1005);
            TESTAbstract test6 = new TEST1(1006);
            TESTAbstract test7 = new TEST2(1007);
            TESTAbstract test8 = new TEST1(1008);

            Console.WriteLine("All StructureItem Elements: " + TESTAbstract.Elements.Count);
            foreach (var item in TESTAbstract.Elements)
            {
                Console.WriteLine("someValue: " + item.someValue + " localId: " + item.localId + " globalId: " + item.globalId);
            }

            Console.WriteLine("TEST1 Elements: " + TEST1.Elements.Count);
            foreach (var item in TEST1.Elements)
            {
                Console.WriteLine("someValue: " + item.someValue + " localId: " + item.localId + " globalId: " + item.globalId);
            }

            Console.WriteLine("TEST2 Elements: " + TEST2.Count);
            foreach (var item in TEST2.Elements)
            {
                Console.WriteLine("someValue: " + item.someValue + " localId: " + item.localId + " globalId: " + item.globalId);
            }

Output:

All StructureItem Elements: 8
someValue: 1001 localId: 0 globalId: 0
someValue: 1002 localId: 0 globalId: 1
someValue: 1003 localId: 1 globalId: 2
someValue: 1004 localId: 1 globalId: 3
someValue: 1005 localId: 2 globalId: 4
someValue: 1006 localId: 3 globalId: 5
someValue: 1007 localId: 2 globalId: 6
someValue: 1008 localId: 4 globalId: 7
TEST1 Elements: 5
someValue: 1001 localId: 0 globalId: 0
someValue: 1004 localId: 1 globalId: 3
someValue: 1005 localId: 2 globalId: 4
someValue: 1006 localId: 3 globalId: 5
someValue: 1008 localId: 4 globalId: 7
TEST2 Elements: 3
someValue: 1002 localId: 0 globalId: 1
someValue: 1003 localId: 1 globalId: 2
someValue: 1007 localId: 2 globalId: 6

The second workaround is using interfaces. This is usefull when you already inherit from some class and want to add static fields.

public abstract class Class2 
{
    public int someValue { get; set; }
}


public class TEST3 : Class2, ITest
{
    public TEST3()
    {
        this.AddObj();

    }

}

public class TEST4 : Class2, ITest
{
    public TEST4()
    {
        this.AddObj();
    }

}

public interface ITest{}

I used then a generic extension method for the interface

public static class Class2Ex
{
    public static void AddObj<T>(this T obj) where T : ITest
    {
        ITest<T>.Instances.Add(obj);
    }
    //Alternative:
    public static List<T> GetOtherInstances<T>(this T obj) where T : ITest
    {
        return ITest<T>.Instances;
    }
}

public static class ITest<T> where T : ITest
{
    public static List<T> Instances = new List<T>();
}

Example:

            TEST3 aTest1 = new TEST3();
            TEST3 aTest2 = new TEST3();
            TEST3 aTest3 = new TEST3();
            TEST3 aTest4 = new TEST3();

            TEST4 aTest5 = new TEST4();
            TEST4 aTest6 = new TEST4();

            Console.WriteLine("TEST4: " + ITest<TEST4>.Instances.Count);
            Console.WriteLine("TEST3: " + ITest<TEST3>.Instances.Count);

Upvotes: 0

Etienne Charland
Etienne Charland

Reputation: 4034

I would create a static constructor in the derived classes.

abstract class A {
  public Image Symbol { get; protected set; }
}
class B:A {
  private static Image SymbolInstance { get; private set; }
  static B() {
    // SymbolInstance = ...
  }
  public B() {
    Symbol = SymbolInstance;
  }
}

The static constructor only gets called a single time when the first instance of class B is initiated. It doesn't matter if you initiate 20 instances.

One detail that would have to be tested: would there be any reference conflict of the static field in Class A when being set from several derived classes, or would each derived class have its own separate instance? I think it should be fine but you'd have to test it.

If you want to enforce the derived class to set the field, you can do a validation in the base class and throw an exception if the value is missing.

Upvotes: -1

Thomas Levesque
Thomas Levesque

Reputation: 292685

It's not possible to make static members abstract/virtual, because polymorphism is based on the actual type of the instance, and static members don't belong to a specific instance.

I think this satisfies all your requirements:

abstract class A
{
    public abstract Image Symbol{get;}
}
class B:A
{

  private static readonly Image _symbol = ...

  public override Image Symbol{get{return _symbol;}}
}
  • each derived class has to implement Symbol, since it's abstract in the base class
  • the static field is readonly, so you can't change it after initialization
  • the instance doesn't contain the image instance, it's shared across all instances of the class
  • (not sure what you mean about the last one, though...)

Of course, the downside is that it requires all derived class to adhere to this pattern. This doesn't prevent a derived class from violating some of the requirements.

Upvotes: 4

Black0ut
Black0ut

Reputation: 1737

You can cache your images by key in a dedicated class and just provide a key for each inherited class :

public class ImageManager
{
    private static Dictionary<string, Image> _images = new Dictionary<string, Image>();
    private static object _locker = new object();
    public static Image GetImageByTypeKey(string key)
    {
        if (!_images.ContainsKey(key))
        {
            lock (_locker) {
                if (!_images.ContainsKey(key))
                {
                    //Add your real loading logic here + handle exceptions (not found, etc...)
                    Image image = Image.FromFile(key + ".png");
                    _images.Add(key,image);
                }
            }
        }
        return _images[key];
    }
}

public abstract class BaseType
{
    public Image Symbol
    {
        get
        {
            return ImageManager.GetImageByTypeKey(SymbolKey);
        }
    }
    protected abstract string SymbolKey { get;  }
}

public class TypeA : BaseType
{
    protected override string SymbolKey
    {
        get { return "TypeA";}
    }
}

Upvotes: 0

Related Questions