字典的多态键

时间:2014-03-21 14:32:39

标签: c# .net dictionary polymorphism

我有一个类的层次结构:

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 + ")";
    }
}

不同的键:

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)
    {
    }
}

我有一本字典IDictionary<Key, Data>。我想实现一种方法来查找字典中最相关的数据:

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();
}

是否可以在没有反思的情况下实施MostRelevantData?如果它更容易,可以重新设计Key类。

编辑:示例:

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"

2 个答案:

答案 0 :(得分:3)

您可以向Key添加方法以获取基本密钥实例:

public class Key
{
    public virtual Key GetBaseKey()
    { return null; }
}

public class CarKey : Key
{
    public override Key GetBaseKey()
    { return new Key(this.Name); }
}

然后您的搜索可以写成:

public Data MostRelevantData(Key key)
{
  while(key != null)
  {
    if(dict.Contains(key)) return dict[key];
    key = key.GetBaseKey();
  }
  throw new KeyNotFound();
}

缺点是你必须有效地复制每个子类中的密钥层次结构。

答案 1 :(得分:0)

在没有反思的情况下实施MostRelevantData非常容易。这是一个扩展方法:

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();
    }
}

但是,显然,您需要实现Key.IsRelevant才能实现此功能。幸运的是,这也是非常直接的,由Type.IsSubclassOf方法提供:

public class Key
{
    public bool IsRelevant(Key other)
    {
        return (this.GetType().IsSubclassOf(other.GetType()))
            && (this.Name == other.Name);
    }
}

通过上面的实现,以下Visual Studio单元测试所有传递:

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")));
    }
}