58

I was thinking to use Tuple class to store 2 integer information (StartAddress, EndAddress) I need in my program.

But I discover that Tuple items are ReadOnly, so if I need to set a value for an item, I need to re-instantiate a Tuple.

What is the reason behind this design decision?

Drake
  • 8,225
  • 15
  • 71
  • 104
  • 1
    I think in OO programming, Tuple is usually just a developers laziness to describe his data structures. In functional programming however it is the golden calf. I'm not saying either is good or bad and I'm also lazy some times. Just that there may be different best practices to different use cases. – Robert Cutajar May 10 '13 at 12:54
  • 2
    @Rbjz After learning a lot of advanced Tuple techs and finding out that Tuple is readonly, finally realize that create your own class with 2 property is a better way if you use that structure at many places. – jw_ Oct 04 '19 at 02:40

4 Answers4

56

Tuples originated in functional programming. In (purely) functional programming, everything is immutable by design - a certain variable only has a single definition at all times, as in mathematics. The .NET designers wisely followed the same principle when integrating the functional style into C#/.NET, despite it ultimately being a primarily imperative (hybrid?) language.

Note: Though I suspect the fact that tuples are immutable doesn't really make your task much harder, there are also anonymous types (or perhaps just a simple struct) you might want to use.

Noldorin
  • 144,213
  • 56
  • 264
  • 302
2

I wonder why there is not such thing like this. However, it is what I prefer to use.

namespace System
{
    /// <summary>
    /// Helper so we can call some tuple methods recursively without knowing the underlying types.
    /// </summary>
    internal interface IWTuple
    {
        string ToString(StringBuilder sb);
        int GetHashCode(IEqualityComparer comparer);
        int Size { get; }
    }

    /// <summary>
    /// Represents a writable 2-tuple, or pair.
    /// </summary>
    /// <typeparam name="T1">The type of the tuple's first component.</typeparam>
    /// <typeparam name="T2">The type of the tuple's second component.</typeparam>
    public class WTuple<T1, T2> : IStructuralEquatable, IStructuralComparable, IComparable, IWTuple
    {
        private T1 _item1;
        private T2 _item2;

        #region ImplementedInterfaces
        Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
        {
            return comparer.GetHashCode(_item1);
        }
        Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) {
            if (other == null) return false;
            WTuple<T1, T2> objTuple = other as WTuple<T1, T2>;//Tuple<t1, t2=""> objTuple = other as Tuple<t1, t2="">;
            if (objTuple == null) {
                return false;
            }
            return comparer.Equals(_item1, objTuple._item1) && comparer.Equals(_item2, objTuple._item2);
        }
        Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer)
        {
            if (other == null) return 1;
            WTuple<T1, T2> objTuple = other as WTuple<T1, T2>;//Tuple<t1, t2=""> objTuple = other as Tuple<t1, t2="">;
            if (objTuple == null)
            {
                throw new ArgumentException("ArgumentException_TupleIncorrectType", "other");//ArgumentException(Environment.GetResourceString("ArgumentException_TupleIncorrectType", this.GetType().ToString()), "other");
            }
            int c = 0;
            c = comparer.Compare(_item1, objTuple._item1);
            if (c != 0) return c;
            return comparer.Compare(_item2, objTuple._item2);
        }
        Int32 IComparable.CompareTo(Object obj)
        {
            return ((IStructuralComparable)this).CompareTo(obj, Comparer<object>.Default);
        }
        Int32 IWTuple.GetHashCode(IEqualityComparer comparer)
        {
            return ((IStructuralEquatable)this).GetHashCode(comparer);
        }
        string IWTuple.ToString(StringBuilder sb)
        {
            sb.Append(_item1);
            sb.Append(", ");
            sb.Append(_item2);
            sb.Append(")");
            return sb.ToString();
        }
        int IWTuple.Size
        {
            get { return 2; }
        }
        #endregion

        #region WTuple
        /// <summary>
        /// Initializes a new instance of the System.WTuple&lt;T1,T2&gt; class.
        /// </summary>
        /// <param name="item1">The value of the tuple's first component.</param>
        /// <param name="item2">The value of the tuple's second component.</param>
        public WTuple(T1 item1, T2 item2)
        {
            _item1 = item1;
            _item2 = item2;
        }
        /// <summary>
        /// Gets or sets the value of the current System.WTuple&lt;T1,T2&gt; object's first component.
        /// </summary>
        public T1 Item1
        {
            get { return _item1; }
            set { _item1 = value; }
        }
        /// <summary>
        /// Gets or sets the value of the current System.WTuple&lt;T1,T2&gt; object's second component.
        /// </summary>
        public T2 Item2
        {
            get { return _item2; }
            set { _item2 = value; }
        }
        /// <summary>
        /// Returns a value that indicates whether the current System.WTuple&lt;T1,T2&gt; object
        /// is equal to a specified object.
        /// </summary>
        /// <param name="obj">The object to compare with this instance.</param>
        /// <returns>true if the current instance is equal to the specified object; otherwise,
        /// false.</returns>
        public override Boolean Equals(Object obj)
        {
            return ((IStructuralEquatable)this).Equals(obj, EqualityComparer<object>.Default);
        }
        /// <summary>
        /// Returns the hash code for the current System.WTuple&lt;T1,T2&gt; object.
        /// </summary>
        /// <returns>A 32-bit signed integer hash code.</returns>
        public override int GetHashCode()
        {
            return ((IStructuralEquatable)this).GetHashCode(EqualityComparer<object>.Default);
        }
        /// <summary>
        /// Returns a string that represents the value of this System.WTuple&lt;T1,T2&gt; instance.
        /// </summary>
        /// <returns>The string representation of this System.WTuple&lt;T1,T2&gt; object.</returns>
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("(");
            return ((IWTuple)this).ToString(sb);
        }
        #endregion
    }
}
xamid
  • 440
  • 11
  • 25
  • 1
    This code does absolutely nothing that uses the struct it inherits from but set the underlying Tuple's values once the constructor ... where they remain invariant, and unchangeable. The use of the 'new operator in front of the Properties is unnecessary, and has no effect. Change the code in the setter for 'Item1 to this, and set the value a view times to see that the values in the base are never changed: set { _item1 = value; Console.WriteLine(_item1.ToString()); Console.WriteLine(base.Item1.ToString()); } – BillW Apr 05 '14 at 05:08
  • Thanks for mentioning this. I'm sorry. Actually, I never tested the overridden functions, but only used Item1 and Item2 (which obviously works). I figured out that it would be impossible anyways to change entries in Tuple, due to its use of readonly attributes, as I saw here: http://reflector.webtropy.com/default.aspx/4@0/4@0/untmp/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/clr/src/BCL/System/Tuple@cs/1305376/Tuple@cs However, I updated the code, such that it just implements the interfaces in a similar way to Tuple. – xamid Apr 20 '14 at 11:21
1

You might want to try upgrading, as C# 7.0 introduces new language support for Tuples which (among other things) makes them mutable. (Under the hood, it uses ValueTuple, whose members are mutable.)

Jonathan
  • 32,202
  • 38
  • 137
  • 208
  • Though note that `ValueTuple` won't treat its members as properties but fields. That [can be bad](https://stackoverflow.com/questions/76175008/cant-successfully-bind-observablecollection-of-tuples-to-xaml-vs-extension?noredirect=1#comment134336111_76175008). – ruffin May 04 '23 at 20:41
-3

You got only the getters of the ItemX properties, that's right, but I found a way to first instanciate a tupple with empty values and fill them afterwords.

If you do something like this :

Dictionary <string, Tuple<string, string>> mydic = new  Dictionary<string,Tuple<string,string>>(); 
Tuple<string, string> tplTemp = new Tuple<string, string>("", "");
 mydic.TryGetValue("akey", out tplTemp);

The tplTemp passed as an out parameter will have it's 2 items values from the collection. So that's a way of doing in case this can help's someone.

Greg
  • 19
  • 2
  • 5
    This is not a way to fill a Tuple with values. All you have done is create a second Tuple and assigned tplTemp to it's value. You code is equivalent to just doing: tplTemp = new Tuple("some", "values"); – gerrard00 Jul 13 '15 at 18:27