【问题标题】:Need advise for DataStructure like Tuple or keyValuePair but mutable type需要数据结构的建议,例如 Tuple vs keyValuePair 但可变类型
【发布时间】:2021-02-03 03:51:48
【问题描述】:

我需要一个像TupleKeyValuePair 这样的类型,并且可以选择设置它的值(可变类型)。 现有泛型有什么想法可以满足这些需求吗?

(在我看来,字典对我来说有点矫枉过正)

我也可以编写自己的类,但我希望已经实现了一些可变类型的KeyValuePair

【问题讨论】:

    标签: c# generics data-structures key-value


    【解决方案1】:

    使用可变变量的问题在于,您可以很容易地最终引用同一个元组多次,当您在一个地方更改值时,它也会在您不期望的其他地方更改。因此,如果您确实使用可变元组,则必须非常严格地在适当的位置克隆元组,例如将其放入集合中。

    但是可变元组看起来像这样:

    public class MutableTuple<T1, T2> {
        public T1 Item1 { get; set; }
        public T2 Item2 { get; set; }
    
        public MutableTuple(T1 item1, T2 item2) {
            Item1 = item1;
            Item2 = item2;
        }
    }
    

    【讨论】:

    • 还不够。默认情况下,元组具有相等和哈希码实现。
    【解决方案2】:
    public class MutableKeyValuePair<TKey, TValue>
    {
        public TKey Key { get; set; }
        public TValue Value { get; set; }
    }
    

    就是这样。

    【讨论】:

      【解决方案3】:

      不,没有。除非你认为object[] 是其中之一:-)

      请注意,实现MutableTuple&lt;T1, ...&gt; 非常简单。

      MutableTuple&lt;T1, T2, T3&gt; 的完整实现,几乎等同于 Tuple&lt;T1, T2, T3&gt;(各种接口是公开实现的,而不是私有实现的)(部分基于 Tuple&lt;&gt; 的 Mono 实现)

      public static class MutableTuple
      {
          public static MutableTuple<T1, T2, T3> Create<T1, T2, T3>(T1 item1, T2 item2, T3 item3)
          {
              return new MutableTuple<T1, T2, T3>(item1, item2, item3);
          }
      }
      
      [Serializable]
      public class MutableTuple<T1, T2, T3> : IComparable, IStructuralEquatable, IStructuralComparable
      {
          public T1 Item1 { get; set; }
          public T2 Item2 { get; set; }
          public T3 Item3 { get; set; }
      
          public MutableTuple(T1 item1, T2 item2, T3 item3)
          {
              this.Item1 = item1;
              this.Item2 = item2;
              this.Item3 = item3;
          }
      
          public override bool Equals(object obj)
          {
              return this.Equals(obj, EqualityComparer<object>.Default);
          }
      
          public override int GetHashCode()
          {
              return this.GetHashCode(EqualityComparer<object>.Default);
          }
      
          public override string ToString()
          {
              var sb = new StringBuilder();
      
              sb.Append(this.Item1);
              sb.Append(", ");
              sb.Append(this.Item2);
              sb.Append(", ");
              sb.Append(this.Item3);
              sb.Append(")");
      
              return sb.ToString();
          }
      
          public int CompareTo(object obj)
          {
              return this.CompareTo(obj, Comparer<object>.Default);
          }
      
          public bool Equals(object obj, IEqualityComparer comparer)
          {
              if (obj == null)
              {
                  return false;
              }
      
              var tuple = obj as MutableTuple<T1, T2, T3>;
              return tuple != null && (comparer.Equals(this.Item1, tuple.Item1) && comparer.Equals(this.Item2, tuple.Item2)) && comparer.Equals(this.Item3, tuple.Item3);
          }
      
          public int GetHashCode(IEqualityComparer comparer)
          {
              unchecked
              {
                  int hash = 17;
      
                  hash = (hash * 23) + comparer.GetHashCode(this.Item1);
                  hash = (hash * 23) + comparer.GetHashCode(this.Item2);
                  hash = (hash * 23) + comparer.GetHashCode(this.Item3);
      
                  return hash;
              }
          }
      
          public int CompareTo(object obj, IComparer comparer)
          {
              if (obj == null)
              {
                  return 1;
              }
      
              var other = obj as MutableTuple<T1, T2, T3>;
      
              if (other == null)
              {
                  throw new ArgumentException();
              }
      
              int res = comparer.Compare(this.Item1, other.Item1);
      
              if (res != 0)
              {
                  return res;
              }
      
              res = comparer.Compare(this.Item2, other.Item2);
      
              if (res != 0)
              {
                  return res;
              }
      
              res = comparer.Compare(this.Item3, other.Item3);
      
              return res;
          }
      }
      

      【讨论】:

      • 存在DictionaryEntry,这是一个可变对,但它有两个问题:(1)它不是泛型/类型安全的。 (2) 是可变值类型。
      • @JeppeStigNielsen 你是对的......我不知道。我们甚至可以补充一点,一个问题是它是struct :-) 对于 Tuple,他们非常努力地考虑并选择了classes。第二个(它是一个可变值类型)是一个特性,而不是一个错误:-)
      • 一个很好的完整答案。很少有 cmets,1)你为什么使用非通用比较器来进行相等性和哈希码来导致装箱? 2) 字符串生成器是否值得代替 6 个字符串连接? 3)Mutuple 会是一个更好的命名:D
      • @nawfal 1) 因为接口IStructuralEquatable 就是这样完成的。 2)可能是的,但这取决于子字符串的长度。例如,如果this.Item1 == this.Item2 == this.Item3 == "" 那么简单的字符串连接会更好。但请记住,这是一个代码示例。您必须为各种数量的参数实现它(Tuple&lt;...&gt; 最多可达 8 个)。使用 8 个参数,StringBuilder 会更快。 3)我不是很擅长命名。当我工作时,我尽量让我的同事或我的老板选择名字。
      • 1) IStructuralEquatable 可能是这样,但在不改变结果行为的情况下以通用方式处理 public override bool Equals(object obj) 不是更好吗?但我看到框架本身实现了你所展示的:(。3)我是在开玩笑:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-12-16
      • 1970-01-01
      • 1970-01-01
      • 2014-09-26
      • 2010-12-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多