Reputation: 105029
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.
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...
Upvotes: 8
Views: 2531
Reputation: 10201
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
Upvotes: 0
Reputation: 64487
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.
Upvotes: 2
Reputation: 6636
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
Upvotes: 0
Reputation: 564433
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.
Upvotes: 6
Reputation: 63338
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!
Upvotes: 9