字典避免使用相同的键值类型进行反转

时间:2014-09-23 08:00:25

标签: c# dictionary

我使用dictionary<string,string>。 Key是一个url,Value是一个名字。

由于类型相同,我寻找一种方法来避免键,值反转时的编码错误。 我想要这样的Dictionary<UrlString,NameString>,但是我无法创建自己继承自string的类型,因为它是密封的。

那么有一个简单的方法吗? 感谢

2 个答案:

答案 0 :(得分:2)

首先,您可以使用Uri

http://msdn.microsoft.com/en-us/library/system.uri(v=vs.110).aspx

但是,您可以设计您自己的班级,这是您唯一需要做的事情 是为了将类用作EqualsGetHashCode 词典键

// You can implement in the same manner whatever domain you want:
// Url, Size, Id, Voltage etc.
public sealed class UrlString {
  public UrlString(String address) {
    if (Object.ReferenceEquals(null, address))
      throw new ArgumentNullException("address");

    Address = address;
  }  

  public String Address {
    get;
    private set;
  }

  public override Boolean Equals(Object obj) {
    if (Object.ReferenceEquals(obj, this))
      return true;

    UrlString other = obj as UrlString;

    if (Object.ReferenceEquals(other, null))
      return false;

    return String.Equals(Address, other.Address, StringComparison.OrdinalIgnoreCase);
  }

  public override int GetHashCode() {
    return String.IsNullOrEmpty(Address) ? 0 : Address.ToUpperInvariant().GetHashCode();
  }
}

答案 1 :(得分:0)

一般来说,为业务功能定制集合不是一个好习惯,实现一些自定义方法会更好,但你当然可以。

我至少可以找到两种方法。 1.使用带有隐式转换运算符的自定义结构,它执行检入构造函数。适用于价值类型。 2.使用IEqualityComparer,这是更好的,因为在不改变初始IDictionary签名的情况下工作。

以下是包含两种变体测试的代码示例:

using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace LimitingUseOfValueTypeAsDictionaryKey
{
    public struct MySpecialInt
    {
        public int Target;

        public MySpecialInt(int source)
        {
            if (source == 123)
            {
                throw new ArgumentOutOfRangeException("source");
            }
            Target = source;
        }

        public static implicit operator int(MySpecialInt source)
        {
            return source.Target;
        }

        public static implicit operator MySpecialInt(int source)
        {
            return new MySpecialInt(source);
        }
    }

    public class LimitingIntComparer : IEqualityComparer<int>
    {
        public int Compare(int x, int y)
        {
            return x.CompareTo(y);
        }

        public bool Equals(int x, int y)
        {
            return x.Equals(y);
        }

        public int GetHashCode(int source)
        {
            if (source == 123)
            {
                throw new ArgumentOutOfRangeException("source");
            }
            return source.GetHashCode();
        }
    }

    public class LimitingStringComparer : IEqualityComparer<string>
    {
        public bool Equals(string x, string y)
        {
            return object.Equals(x, y);
        }

        public int GetHashCode(string source)
        {
            if (source == "123")
            {
                throw new ArgumentOutOfRangeException("source");
            }
            return source.GetHashCode();
        }
    }

    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void CanTreatMySpecialIntAsRegularInt()
        {
            var a = 1;
            MySpecialInt b = a;
            Assert.AreEqual((int)b, 1);
        }

        [TestMethod]
        public void CanUseOnlyAllowedValuesAsAKeyUsingCustomStruct()
        {
            var d = new Dictionary<MySpecialInt, string>();
            d.Add(1, "foo");

            try
            {
                d.Add(123, "bar");
            }
            catch (ArgumentOutOfRangeException)
            {
                Console.WriteLine("Can't do that");
            }

            Assert.AreEqual(1, d.Count);
            Assert.AreEqual("foo", d[1]);
        }

        [TestMethod]
        public void CanUseOnlyAllowedValuesAsAKeyUsingUsingComparer()
        {
            var d = new Dictionary<int, string>(new LimitingIntComparer());
            d.Add(1, "foo");

            try
            {
                d.Add(123, "bar");
            }
            catch (ArgumentOutOfRangeException)
            {
                Console.WriteLine("Can't do that");
            }

            Assert.AreEqual(1, d.Count);
            Assert.AreEqual("foo", d[1]);
        }

        [TestMethod]
        public void CanUseOnlyAllowedValuesAsAKeyUsingUsingComparerForStrings()
        {
            var d = new Dictionary<string, string>(new LimitingStringComparer());
            d.Add("1", "foo");

            try
            {
                d.Add("123", "bar");
            }
            catch (ArgumentOutOfRangeException)
            {
                Console.WriteLine("Can't do that");
            }

            Assert.AreEqual(1, d.Count);
            Assert.AreEqual("foo", d["1"]);
        }
    }
}