Skip to content

[Proposal] Add a family of covariant System.ITuple<out T1, out T2, ...> interfaces #1135

@VladimirReshetnikov

Description

@VladimirReshetnikov

Currently we have 2 families of types to represent tuples: classes System.Tuple<T1, T2, ...> and mutable structs System.ValueTuple<T1, T2, ...>. There are a family of extension methods to convert between them: System.TupleExtensions.ToTuple<...>(...) and System.TupleExtensions.ToValueTuple<...>(...). Both families of tuples are already stuck in some APIs for good. Some users have their own tuple types from before tuples were introduced into the standard library.

To improve interoperability, avoid making unnecessary copies, and support covariant conversions, I propose to introduce a family of covariant interfaces System.ITuple<out T1, out T2, ...> that will be implemented both by Tuple<...> and ValueTuple<...>. Users also can make their custom tuple types implement these interfaces. Here is a sketch of how they might look:

using System;
using System.Collections;

namespace System
{
    // Inherit interfaces shared by both Tuple<...> and ValueTuple<...> types
    // They are not strictly necessary; it is debatable whether they are needed here
    public interface ITuple<out T1, out T2> : IStructuralEquatable, IStructuralComparable, IComparable, ITuple
    {
        T1 Item1 { get; }
        T2 Item2 { get; }
    }

    public class Tuple<T1, T2> : ITuple<T1, T2>, ITupleInternal
    {
        public T1 Item1 => this.m_Item1; // already exists
        public T2 Item2 => this.m_Item2; // already exists
        // other existing members...
    }

    public struct ValueTuple<T1, T2> : ITuple<T1, T2>, IEquatable<ValueTuple<T1, T2>>, IComparable<ValueTuple<T1, T2>>, IValueTupleInternal
    {
        T1 ITuple<T1, T2>.Item1 => this.Item1; // explicit interface implementation to avoid conflict with existing field Item1
        T2 ITuple<T1, T2>.Item2 => this.Item2; // explicit interface implementation to avoid conflict with existing field Item2
        // other existing members...
    }
}

The existing static class System.TupleExtensions could add extension methods to support these new interfaces:

namespace System
{
    public static class TupleExtensions
    {
        public static (T1, T2) ToValueTuple<T1, T2>(this ITuple<T1, T2> value) =>
            value is null
                ? throw new ArgumentNullException(nameof(value))
                : (value.Item1, value.Item2);

        public static Tuple<T1, T2> ToTuple<T1, T2>(this ITuple<T1, T2> value) =>
            value is null
                ? throw new ArgumentNullException(nameof(value))
                : value as Tuple<T1, T2> ?? Tuple.Create(value.Item1, value.Item2);

        public static void Deconstruct<T1, T2>(this ITuple<T1, T2> value, out T1 item1, out T2 item2)
        {
            if (value is null)
                throw new ArgumentNullException(nameof(value));

            item1 = value.Item1;
            item2 = value.Item2;
        }

        // other existing members...
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-needs-workAPI needs work before it is approved, it is NOT ready for implementationarea-System.Runtime

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions