8

Suppose I have a Generic abstract class that provides some default constructor functionality so you don't have to repeat the code in your inherited classes:

public abstract class Base<TKey, TValue>
{
    private static readonly IDictionary<TKey, Base<TKey, TValue>> Values = new Dictionary<TKey, Base<TKey, TValue>>();

    public TKey Key { get; private set; }
    public TValue Value { get; private set; }

    protected Base(TKey key, TValue value)
    {
        this.Key = key;
        this.Value = value;

        Values.Add(key, this);
    }
}

Now the problem is that when I write two inherited classes like:

public sealed class One : Base<string, string>
{
    public static readonly One Default = new One("DEF", "Default value");
    public static readonly One Invalid = new One("", "Invalid value");

    private One(string key, string value) : base(key, value) {}
}

public sealed class Two : Base<string, string>
{
    public static readonly Two EU = new Two("EU", "European Union");
    public static readonly Two USA = new Two("", "United States of America");

    private Two(string key, string value) : base(key, value) {}
}

As you can see these two are sort of type-safe enumerations really. They only provide predefined values and they can't be instantiated for any other purpose.

Question

The problem is because both of these inherited classes use the same generic types with base class which is Base<string, string>. And the way that generic classes and static fields work is that for each generic type (or combination when more than one) a new static field is created.

In my case the combination is string, string that's why there's only one base class static field and it holds 4 values instead of having two static fields each with 2 values.

How do I overcome this issue and separate those while still keeping this functionality on the base class so I don't repeat my code?

Changing field from static to instance won't work, because they I'll end up with 4 instances each holding just one value...

5
  • 1
    That's why base class static field holds 4 values instead of each of 2 static fields each holding 2 values. wait, where does :base end up with 4 values? Commented Jun 19, 2012 at 15:22
  • 2
    @jcolebrand: Well because they both inherit from Base<string, string> and the way that generic classes and static fields work, this means that only one static field is created. And that is for the Base<string, string>. All values are therefore saved into it even though there are two inheriting classes. Commented Jun 19, 2012 at 15:25
  • Even tho you're instantiating a new each time? I would'a thought the semantics indicated that the static would be static over Two or static over One, not static over base. I presume this is a real problem you're trying to overcome. Why can't you just move those into another subclass that is a property on this? Commented Jun 19, 2012 at 15:28
  • 1
    @jcolebrand The 4 items come from constructing two instances of Two and two instances of One, One and Two will both be looking at the same static Values. Commented Jun 19, 2012 at 15:29
  • @jcolebrand: why don't you write an answer and provide some code, because I'm already crosseyed because of this issue... :) Commented Jun 19, 2012 at 15:29

5 Answers 5

9

This will work. I believe it's known as the curiously recurring template pattern, but don't quote me on that

public abstract class Base<TSelf, TKey, TValue>
{
    private static readonly IDictionary<TKey, Base<TSelf, TKey, TValue>> Values = 
        new Dictionary<TKey, Base<TSelf, TKey, TValue>>();

    public TKey Key { get; private set; }
    public TValue Value { get; private set; }

    protected Base(TKey key, TValue value)
    {
        this.Key = key;
        this.Value = value;

        Values.Add(key, this);
    }
}


public sealed class One : Base<One, string, string>
{
    public static readonly One Default = new One("DEF", "Default value");
    public static readonly One Invalid = new One("", "Invalid value");

    private One(string key, string value) : base(key, value) { }
}

public sealed class Two : Base<Two, string, string>
{
    public static readonly Two EU = new Two("EU", "European Union");
    public static readonly Two USA = new Two("", "United States of America");

    private Two(string key, string value) : base(key, value) { }
}

The 'curious' part is that the definitions of One and Two are allowed to use themselves as type parameters to themselves!

Sign up to request clarification or add additional context in comments.

5 Comments

It does rely on people specifying the TSelf correctly, however.
Yes, it's a hack, but it's important to note that it doesn't enforce anything.
@Robert: what loop are you talking about?
@Groo: This one: Base<Base<Base<..., TKey, TValue>, TKey, TValue>, TKey, TValue> because I wasn't thinking of using third generic type parameter with constraint but rather concrete class...
@Groo: very good link! Actually I was talking about the same infinite loop that's referred to in the article as the circular reference. I wasn't able to get my head around it. :)
6

The problem, of course, arises because your two classes share the same base class with the same generic type arguments. This means they share the same static members.

How do I overcome this issue and separate those while still keeping this functionality on the base class so I don't repeat my code?

One approach would be to build your Dictionary within a dictionary based on the current runtime type:

private static readonly IDictionary<Type, IDictionary<TKey, Base<TKey, TValue>>> Values = new Dictionary<Type, IDictionary<TKey, Base<TKey, TValue>>()>;


protected Base(TKey key, TValue value)
{
    this.Key = key;
    this.Value = value;

    IDictionary<TKey, Base<TKey, TValue>> dict;
    if (!Values.TryGetValue(this.GetType(), out dict)
    { 
        dict = new Dictionary<TKey, Base<TKey, TValue>>();
        Values[this.GetType()] = dict;
    }

    dict.Add(key, this);
}

This does cause all lookups to be 2 phases, as you have to lookup the current type then lookup the value within that dictionary, but it doesn't rely on changing your subclass API at all.

12 Comments

+1 That's what I wanted to mention. Actually, this static dictionary can be part of a completely different generic class with takes the inherited classes' type as a parameter.
It's worth noting that this would still be one static dictionary per resolved generic type, just keyed differently.
@AdamHouldsworth Yes - though now it's keyed per subclass, so you "effectively" get what the OP wants. It's a hack, but it directly answers the OP's question
@ReedCopsey Yes I agree, I was just clarifying the difference between this and the other answer that would actually result in two static variables as the generic types resolve differently.
I was thinking of implementing this as well, but instead of having nested dictionaries I'd concatenate type with key, since I'm using strings as keys...
|
2

This doesn't answer the question directly, but instead provides an alternative design to hopefully achieve the same objective.

It seems to me like you are trying to use a base class for something that isn't base behaviour, or at the very least isn't static behaviour.

Separate the concerns of being a lookup list item and providing the lookup list reference.

If you are trying to provide known lookups, I would make the lookup list reference separate to the lookup items (as in, not a static member):

public static class MyLookups
{
    public static IDictionary<string, string> Acronyms = new Dictionary<string, string>();
    public static IDictionary<string, string> DefaultValues = new Dictionary<string, string>();
}

Then each type can simply point at the relevant dictionary, or name it and access it via reflection, or provide a Func to it:

public class One : Base<string, string>
{
    public One(string key, string value)
        : base(key, value, () => MyLookups.DefaultValues)
    {
    }
}

public class Two : Base<string, string>
{
    public Two(string key, string value)
        : base(key, value, () => MyLookups.Acronyms)
    {
    }
}

public abstract class Base<TKey, TValue>
{
    private IDictionary<TKey, TValue> _dictionaryReference;

    protected Base(TKey key, TValue value, Func<IDictionary<TKey, TValue>> getDictionary) 
    {
        _dictionaryReference = getDictionary();
        _dictionaryReference.Add(key, value);
    }
}

This will also then let you separate the list itself from the items in the list (via injection perhaps).

Also note that if the base class instances keep a reference to the same dictionaries, there will be no need to have the member static.

Comments

0

This is by design. A static member will only have one copy across all instances, and a static member in a base class will only have one copy across all instances of all of its subclasses.

Why are you static? Do you actually want all instances of One to share a common dictionary?

If you want One to share across Ones, but not share with Twos, then you will need to change your design. Either make it not static (which will cause no sharing), or make it static at the level of each sub-class.

Make an abstract method/property in the base class and override in the subclasses if you want to keep the API the same between them

Comments

0

Make your base differ.

public abstract class Base<T, TKey, TValue> where T : Base<T, TKey, TValue>
{ 
    private static readonly IDictionary<TKey, T> Values = 
        new Dictionary<TKey, T>(); 

    public TKey Key { get; private set; } 
    public TValue Value { get; private set; } 

    protected Base(TKey key, TValue value) 
    { 
        this.Key = key; 
        this.Value = value; 

        Values.Add(key, (T)this); 
    } 
} 

UPDATE: After posting I noticed more or less the same answer was already given, but notice the difference: the constraint on type T, and the use of T in the dictionary definition. It's not fool proof, but it helps.

The cast to T is required to be able to add the object the the dictionary. Notice that I use an explicit cast, not the as operator. When used incorrectly, the as operator would insert a null into the dictionary, where the cast would break it (as it should).

But see also: http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx

5 Comments

Thank you Kris. I'll upvote it back up, because I also don't know why it got downvoted. Especially because you've simplified dictionary definition. It does save T instances... So +1 from me.
Hmmm. But if you use IDictionary<TKey, T> you can't do Values.Add(key, this)...
Indeed, I missed that. Well, in that case we have to change the dictionary definition. I'd still keep the constraint though. I'll update the answer, though I might delete it as it's now very close to the other one.
Not really.. Since I'll be adding T instances it's safe to change it to: Values.Add(key, this as T). And it works.
true again, except for the as operator; I'd use an explicit cast.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.