C#7 值 tuple/deconstruction 不对称

C#7 value tuple/deconstruction asymmetry

Fiddle here.

给定一个函数(string a, string b) F(),你可以解构它的元组returns:

var (a, b) = F();

(string c, string d) = F();

或者您可以直接分配它:

var (a, b) e = F();

(string a, string b) f = F();

var g = F();  //  One of these things is not like the others.

Class 解构函数的行为类似于第一种情况。给定 class CDeconstructor(out string a, out string b):

var c = new C();

var (h, i) = c;

(string j, string k) = c;

但编译器不会使用解构函数将其隐式转换为元组:

//  Cannot implicitly convert type 'C' to '(string a, string b)'
var (a, b) l = c;

显然你可以基于解构函数机械地写一个隐式转换:

public static implicit operator (string a, string b) (C c)
{
    c.Deconstruct(out string a, out string b);
    return (a, b);
}

尽管解构和赋值情况在语法上有视觉上的相似性,但将引用分配给元组与将 class 解构为变量然后将它们放入新元组中是不同的。但是,您可以将 (int x, int y) 隐式转换为 (double x, double y)。值元组是一种语法糖功能,它看起来就像它所做的那样,而不用管实现细节。

如果我想到这个,C# 团队就想到了,如果他们选择不添加 "magic" 对隐式转换的支持,他们有充分的理由1.

为什么自动进行隐式转换不是一个好主意,是否有积极的理由?

或者它是那些价值不足以证明成本合理的功能之一?


这是来自 that fiddle 的代码:

public class Program
{
    public static void Main()
    {
        (string a, string b) = F();

        (string a, string b) ab = F();

        Console.WriteLine($"a: {a} b: {b} ab: {ab}");


        var c = new C();

        (string d, string e) = c;

        //  Cannot implicitly convert type 'C' to '(string a, string b)'
        (string a, string b) f = c;

        Console.WriteLine($"d: {d} e: {e} f: {f}");

        //  Covariance
        (object c, object d) g = F();
        //  Implicit conversion
        (double x, double y) t = G();
    }

    public static (string a, string b) F() 
        => ("A", "B");

    public static (int x, int y) G() 
        => (0, 1);
}

public class C
{
    public String A = "A";
    public String B = "B";

    public void Deconstruct(out String a, out String b)
    {
        a = A;
        b = B;
    }
}

1 C# 团队可能并不比每个人都聪明,但我从来没有输过赌他们至少和我一样聪明。

(string a, string b) ab 声明了一个名为 ab 的元组类型 (string a, string b) 单个 变量。这使您可以写 ab.aab.b,但不能写 ab

(string a, string b) f = c; 尝试将不相关的 C 类型转换为该元组类型。除非你写演员,否则不可能发生这种情况。

具体来说,顾名思义,解构只允许你赋值给变量;它不允许您转换为不相关的类型。

让解构像隐式转换器一样工作的能力是在 C# 7 发布之前(由我提出,所以我在这里有偏见)所要求的。团队的回应是(正如我所读到的那样)它被要求太接近 C# 7 版本并且实施起来会花费太长时间,因此没有考虑。因为它现在是一个重大变化,所以它永远不会发生。

有关此事的实际讨论,请参阅“Allow Deconstruct and implicit operator to both support deconstruction and conversion to tuple types”roslyn 回购问题。

这是实现您想要实现的目标的一种方法:在您的代码示例中,使用 (string a, string b) f = (_, _) = c;.

而不是 (string a, string b) f = c;

您还可以编写一个用户定义的从您的类型到您需要的元组类型的转换。