Деконструкция неоднозначна

У меня есть векторный класс с двумя методами деконструкции следующим образом:

public readonly struct Vector2
{
    public readonly double X, Y;

    ...

    public void Deconstruct( out double x, out double y )
    {
        x = this.X;
        y = this.Y;
    }

    public void Deconstruct( out Vector2 unitVector, out double length )
    {
        length = this.Length;
        unitVector = this / length;
    }
}

Где-то еще у меня есть:

Vector2 foo = ...
(Vector2 dir, double len) = foo;

Это дает мне:

CS0121: The call is ambiguous between the following methods or properties: 'Vector2.Deconstruct(out double, out double)' and 'Vector2.Deconstruct(out Vector2, out double)'

Как это неоднозначно?

Редактировать: вызов Deconstruct вручную работает нормально:

foo.Deconstruct( out Vector2 dir, out double len );

Ответы

Ответ 1

Это по замыслу в С#. Перегрузки Deconstruct должны иметь различную арность (количество параметров), в противном случае они неоднозначны.

Сопоставление с образцом не имеет левой стороны. Более сложная схема сопоставления с образцом состоит в том, чтобы иметь список сопоставляемых образцов в скобках, и мы используем количество шаблонов, чтобы решить, какой Deconstruct использовать. - Нил Гафтер https://github.com/dotnet/csharplang/issues/1998#issuecomment-438472660

Ответ 2

Быстрый отказ от ответственности - у меня нет С# 7.3, и я не видел этот шаблон раньше.

Тем не менее, я подозреваю, что это может сработать...

public readonly struct Vector2
{
    public readonly double X, Y;

    public DeconstructHelper<double, double> To_X_and_Y
    {
        get
        {
            return new DeconstructHelper<double, double>(
                        this.X
                    ,   this.Y
                );
        }
    }

    public DeconstructHelper<Vector2, double> To_Unit_and_Length
    {
        get
        {
            var length = this.Length;
            return new DeconstructHelper<Vector2, double>(
                        length
                    ,   this / length
                );
        }
    }

    public class DeconstructHelper<T_Arg1, T_Arg2>
    {
        protected T_Arg1 Argument1 { get; private set; }
        protected T_Arg2 Argument2 { get; private set; }
        public DeconstructHelper(
                    T_Arg1 argument1
                ,   T_Arg2 argument2
            )
        {
            this.Argument1 = argument1;
            this.Argument2 = argument2;
        }

        public void Deconstruct(
                    out T_Arg1 argument1
                ,   out T_Arg2 argument2
            )
        {
            argument1 = this.Argument1;
            argument2 = this.Argument2;
        }
    }
}

Тогда, чтобы назвать это, было бы

var vector = new Vector2( /* ... */ );

(var x, var y) = vector.To_X_and_Y;
(var unitVector, var length) = vector.To_Unit_and_Length;

Общая идея получить get -property для создания объекта с версией .Deconstruct() вы хотите.