【问题标题】:Building a generic collection class构建通用集合类
【发布时间】:2011-04-09 01:12:33
【问题描述】:

我正在构建以下类来管理字典。

    public class EnumDictionary<TKey, TValue>
    {
        private Dictionary<TKey, TValue> _Dict;

        public EnumDictionary(Dictionary<TKey, TValue> Dict)
        {
            this._Dict = Dict;
        }

        public TKey GetValue(TValue value)
        {
            foreach (KeyValuePair<TKey, TValue> kvp in _Dict)
            {
                if (kvp.Value == value)
                    return kvp.Key;
            }

            throw new Exception("Undefined data type: " + value);
        }              
    }

但我收到错误消息“运算符 '==' 不能应用于 'TValue' 和 'TValue' 类型的操作数”。

顺便说一句,我制作这个自定义集合是因为​​我的字典具有唯一值,但我无法从字典中逐个键值获取。

感谢任何帮助。谢谢。

【问题讨论】:

    标签: c# collections dictionary generics


    【解决方案1】:

    您是否尝试过使用Equals 方法?

    if (kvp.Value.Equals(value))
    

    我认为这个限制是由于== 运算符不能用于所有类型。以以下为例:

    struct Test
    {
        public int Value;
    }
    

    鉴于上述结构,以下代码将无法编译:

    Test a, b;
    a = b = new Test();
    bool areEqual = a == b; // Operator '==' cannot be applied to 
                            // operands of type 'Test' and 'Test'
    

    但是,所有类型都有Equals 方法,所以调用它会起作用:

    Test a, b;
    a = b = new Test();
    bool areEqual = a.Equals(b);
    

    【讨论】:

      【解决方案2】:

      当您使用通用比较时,我认为您应该实现 (x)CompareTo(Y) 或可比较的类。如果我错了,请纠正我。

      【讨论】:

      • 只有在需要 比较时才需要这个。如果你这样做了,那么你应该实现 IComparable 接口和 CompareTo() 方法。否则 Equals() 方法(从 Object 继承并可能被覆盖)工作正常。
      • 感谢您为我解决这个问题。自从我从事这种事情以来已经有一段时间了。
      【解决方案3】:

      在泛型类型上使用“where”条件

      class Dictionary<TKey,TVal>
          where TKey: IComparable, IEnumerable
          where TVal: MyI
      {
          public void Add(TKey key, TVal val)
          {
          }
      }
      

      来自http://msdn.microsoft.com/en-us/library/6b0scde8%28VS.80%29.aspx

      【讨论】:

        【解决方案4】:

        您可以使用 if (kvp.Value.Equals(value)) 代替 ==。

        【讨论】:

          【解决方案5】:

          Fredrik is right;您需要使用Equals,因为您不能假定所有类型都可以使用==,因为不是为每种类型都定义了运算符。

          根据您的情况,添加也可能有意义

          where TValue : IEquatable<TValue>
          

          作为类的泛型类型约束。这样做的原因是object.Equals 接受另一个object 作为参数,这意味着如果TValue 是一个值类型,它将被装箱。另一方面,如果可以知道实现IEquatable&lt;TValue&gt;,那么Equals 可以解析为IEquatable&lt;TValue&gt;.Equals*,它将TValue 作为参数,因此不需要对值类型进行装箱。

          我可能建议你重新考虑这个类的内部结构。就目前而言,根本没有理由需要这个类,因为您可以轻松地向IDictionary&lt;TKey, TValue&gt; 添加一个扩展方法,以通过对值的枚举按值查找键。我要做的是存储两个字典:Dictionary&lt;TKey, TValue&gt;Dictionary&lt;TValue, TKey&gt;,以便在 O(1) 中进行双向查找。

          *顺便说一句,如果你好奇,你不能使用IEquatable&lt;T&gt;(或任何接口)来确保一个类型已经实现了==操作符的原因是运算符是静态的,接口不能提供静态方法(因此不能提供运算符)。

          【讨论】:

          • 你试过了吗?奇怪的是,除非我添加约束,否则我无法让它工作:where TValue : class - ideone.com/qdG73IEquatable&lt;TValue&gt; 也没有编译。
          • @Kobi:找不到工作?听起来你误解了我的意思;如果您添加where TValue : class 约束,您可以让== 工作,因为== 是为所有引用类型隐式定义的(它只会使用object.ReferenceEquals)。我在回答中建议的是 OP 添加 where TValue : IEquatable&lt;TValue&gt; 以便 Equals 方法 不会将其参数装箱。是的,我确实尝试过这个,它按预期对我有用。这很清楚,还是我错过了什么?
          • @Kobi:是的,我查看了您的链接,似乎您认为我在说“如果您添加 where TValue : IEquatable&lt;TValue&gt;,您可以使用 == 运算符”...但这不是我的意思在说。希望现在更清楚了吗?还是你认为我应该改写我的答案?
          • 显然,我误解了你。抱歉,感谢您的额外解释。
          【解决方案6】:

          不要创建新类。创建扩展方法:

          public static class DictionaryHelper
          {
              public static TKey GetKeyFromValue<TKey, TValue>(this IDictionary<TKey, TValue> instance, TValue value)
              {
                  foreach (var kvp in instance)
                  {
                      if (kvp.Value.Equals(value))
                          return kvp.Key;
                  }
                  return default(TKey);
              }
          }
          
          public class Example
          {
              public static void Main(string[] argv)
              {
                  Dictionary<string, string> test = new Dictionary<string, string> { { "Mykey", "MyValue" }, { "Key1", "Value2" } };
                  string key = test.GetKeyFromValue("MyValue");
              }
          }
          

          【讨论】:

            【解决方案7】:

            如果您希望这是通用的,那么您将希望相等的定义是可配置的,就像它在字典中的键一样。

            有一个IEqualityComparer&lt;TValue&gt; 类型的属性,在构造函数中设置。

            然后有一个构造函数的版本,使默认EqualityComparer&lt;TValue&gt;.Default。这将通过在有问题的类型上调用 Equals 来工作。

            public class EnumDictionary<TKey, TValue>
            {
                private Dictionary<TKey, TValue> _Dict;
                private readonly IEqualityComparer<TValue> _cmp;
            
                public EnumDictionary(Dictionary<TKey, TValue> Dict, IEqualityComparer<TValue> cmp)
                {
                    this._Dict = Dict;
                    _cmp = cmp;
                }
                public EnumDictionary(Dictionary<TKey, TValue> Dict)
                    :this(Dict, IEqualityComparer<TValue>.Default){}
            
                public TKey GetValue(TValue value)
                {
                    foreach (KeyValuePair<TKey, TValue> kvp in _Dict)
                    {
                        if (cmp.Equals(kvp.Value, value))
                            return kvp.Key;
                    }
            
                    throw new Exception("Undefined data type: " + value);
                }              
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-11-03
              • 2014-11-27
              • 2012-05-19
              • 2015-10-08
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多