C#7值元组/解构不对称

C#7值元组/解构不对称,c#,c#-7.0,valuetuple,C#,C# 7.0,Valuetuple,给定函数(字符串a、字符串b)F(),可以解构它返回的元组: 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. 类解构器的行为与第一种情况类似。给定一个带有解构器(输出字符串a、输出字符串b)的类C:

给定函数
(字符串a、字符串b)F()
,可以解构它返回的元组:

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.
类解构器的行为与第一种情况类似。给定一个带有解构器(输出字符串a、输出字符串b)的类
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);
}
尽管解构和赋值情况在语法上有视觉上的相似性,但将引用赋值给元组并不等同于将类解构成变量,然后将它们放入新的元组。但是,您可以将
(intx,inty)
隐式转换为
(double x,double y)
。值元组是一种语法糖特性,它做它看起来做的事情,更不用说实现细节了

如果我想到了这一点,C#团队也想到了,如果他们选择不为隐式转换添加“魔法”支持,他们有很好的理由1

自动执行隐式转换是个坏主意,有什么积极的原因吗?

或者它是那些被认为价值不足以证明其成本的功能之一


以下是以下代码:

1 C#团队可能并不比所有人都聪明,但我从未因为他们至少和我一样聪明而输钱

(字符串a,字符串b)ab
声明一个元组类型的变量
(字符串a,字符串b)
名为
ab
。这允许您编写
ab.a
ab.b
,但不能编写
a
b

(字符串a、字符串b)f=c
尝试将不相关的
C
类型转换为此元组类型。除非你写一个演员阵容,否则这是不可能发生的


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

在C#7发布之前,解构像隐式转换器一样工作的能力是我要求的(我在这里有偏见)。团队的反应是(正如我所读到的那样),它被要求离C#7版本太近,实施时间太长,因此没有考虑。由于这将是一个突破性的变化,它不会发生


有关该问题的实际讨论,请参见“roslyn回购问题”。

这里有一种方法可以实现您的目标:在您的代码示例中,而不是
(字符串a,字符串b)f=c,使用
(字符串a,字符串b)f=(u,u)=c


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

显然,您不希望以通常不希望从一个类到另一个类进行隐式转换的方式进行隐式解构。此外,使用同一来源的元组和解构变量也没有多大意义。这只会使代码更难理解。作为一名语言设计师,无论怎样,开始时都应该更加严格,然后最终进行概括,而不是相反。例如,引用类型在默认情况下可以为null的事实是C#现在被认为是最大的设计错误之一……我真的很欣赏这里的技巧!我从来都不想在生产代码中看到这一点,但它很简洁:)
public static implicit operator (string a, string b) (C c)
{
    c.Deconstruct(out string a, out string b);
    return (a, b);
}
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;
    }
}