Reputation: 36649
I have a hierarchy of classes:
public class Key
{
private readonly string _name;
public Key(string name)
{
_name = name;
}
public string Name
{
get { return _name; }
}
public override bool Equals(object obj)
{
if (obj == null) return false;
if (GetType() != obj.GetType()) return false;
Key other = (Key)obj;
return Name == other.Name;
}
public override int GetHashCode()
{
return GetType().GetHashCode() ^ _name.GetHashCode();
}
public override string ToString()
{
return GetType() + "(" + Name + ")";
}
}
Different keys:
public class CarKey : Key
{
public CarKey(string name)
: base(name)
{
}
}
public class VanKey : CarKey
{
public VanKey(string name)
: base(name)
{
}
}
public class CoupeKey : CarKey
{
public CoupeKey(string name)
: base(name)
{
}
}
and I have a dictionary IDictionary<Key, Data>
. I would like to implement a method to find the most relevant data in the dictionary:
IDictionary<Key, Data> dict;
public Data MostRelevantData(Key key)
{
if(dict.Contains(key)) return dict[key];
//try base class of key (with the same name) recursively
throw new KeyNotFound();
}
Is it possible to implement MostRelevantData
without reflection? Feel free to redesign the Key
classes if it makes it any easier.
EDIT: Example:
dict[new Key("A")] = "Data A";
dict[new CarKey("B")] = "Data B";
dict[new CoupeKey("B")] = "Data B1";
dict[new CoupeKey("C")] = "Data C";
MostRelevantData(new CoupeKey("B"));//returns "Data B1"
MostRelevantData(new CoupeKey("A"));//returns "Data A"
MostRelevantData(new CoupeKey("C"));//returns "Data C"
MostRelevantData(new CarKey("C"));//throws
MostRelevantData(new CarKey("B"));//returns "Data B"
MostRelevantData(new VanKey("C"));//throws
MostRelevantData(new VanKey("B"));//returns "Data B"
Upvotes: 0
Views: 770
Reputation: 5132
It is quite easy to implement MostRelevantData
without reflection. Here's an extension method:
public static class DictionaryExtensionMethods
{
public static T MostRelevantData<T>(this IDictionary<Key, T> dict, Key key)
{
if (dict.ContainsKey(key))
{
return dict[key];
}
Key relevantKey = dict.Keys.FirstOrDefault(
loop => key.IsRelevant(loop));
if (relevantKey != null)
{
return dict[relevantKey];
}
throw new KeyNotFoundException();
}
}
Obviously, though, you'll need to implement Key.IsRelevant
for this to work. Fortunately, that's pretty straightforward as well, courtesy of the Type.IsSubclassOf method:
public class Key
{
public bool IsRelevant(Key other)
{
return (this.GetType().IsSubclassOf(other.GetType()))
&& (this.Name == other.Name);
}
}
With the above implementation, the following Visual Studio unit tests all pass:
public class PolymorphicKeysTests
{
public PolymorphicKeysTests()
{
}
private Dictionary<Key, string> dict;
[TestInitialize]
public void TestInitialize()
{
dict = new Dictionary<Key, string>();
dict[new Key("A")] = "Data A";
dict[new CarKey("B")] = "Data B";
dict[new CoupeKey("B")] = "Data B1";
dict[new CoupeKey("C")] = "Data C";
}
[TestMethod]
public void CoupeKeyB()
{
Assert.AreEqual("Data B1", dict.MostRelevantData(new CoupeKey("B")));
}
[TestMethod]
public void CoupeKeyA()
{
Assert.AreEqual("Data A", dict.MostRelevantData(new CoupeKey("A")));
}
[TestMethod]
public void CoupeKeyC()
{
Assert.AreEqual("Data C", dict.MostRelevantData(new CoupeKey("C")));
}
[TestMethod]
[ExpectedException(typeof(KeyNotFoundException))]
public void CarKeyC()
{
dict.MostRelevantData(new CarKey("C"));
}
[TestMethod]
public void CarKeyB()
{
Assert.AreEqual("Data B", dict.MostRelevantData(new CarKey("B")));
}
[TestMethod]
[ExpectedException(typeof(KeyNotFoundException))]
public void VanKeyC()
{
dict.MostRelevantData(new VanKey("C"));
}
[TestMethod]
public void VanKeyB()
{
Assert.AreEqual("Data B", dict.MostRelevantData(new VanKey("B")));
}
}
Upvotes: 0
Reputation: 144136
You could add a method to Key
to get a base key instance:
public class Key
{
public virtual Key GetBaseKey()
{ return null; }
}
public class CarKey : Key
{
public override Key GetBaseKey()
{ return new Key(this.Name); }
}
then your search can be written as:
public Data MostRelevantData(Key key)
{
while(key != null)
{
if(dict.Contains(key)) return dict[key];
key = key.GetBaseKey();
}
throw new KeyNotFound();
}
The downside is you'll have to effectively duplicate the key hierarchy in each subclass.
Upvotes: 3