C#解构和重载

C# deconstruction and overloads

在调查 C# 中的新功能时 7.x,我创建了以下 class:

using System;
namespace ValueTuples
{
    public class Person
    {
        public string Name { get; }
        public DateTime BirthDate { get; }

        public Person(string name, DateTime birthDate)
        {
            Name = name;
            BirthDate = birthDate;
        }

        public void Deconstruct(out string name,
            out int year, out int month, out int day)
        {
            name  = Name;
            year  = BirthDate.Year;
            month = BirthDate.Month;
            day   = BirthDate.Day;
        }

        public void Deconstruct(out string name,
            out int year, out int month, 
            out (int DayNumber, DayOfWeek DayOfWeek) day)
        {
            name = Name;
            year = BirthDate.Year;
            month = BirthDate.Month;
            day.DayNumber = BirthDate.Day;
            day.DayOfWeek = BirthDate.DayOfWeek;
        }
    }
}

以及以下测试代码:

using System;
namespace ValueTuples
{
    class MainClass
    {
        static void Main()
        {
            var dh = new Person("Dennis", new DateTime(1985, 12, 27));
            // DECONSTRUCTION:
            (string name, _, _, (_, DayOfWeek dow)) = dh;
            Console.WriteLine($"{name} was born a {dow}");
        }
    }
}

如果 Person class 仅包含 SECOND Deconstruct 重载,则代码可以正常编译和运行 ("Dennis was born a Friday")。但是一旦将第一个重载添加到 Person,编译器就会开始报错,错误消息为:

The call is ambiguous between the following methods or properties: 'Person.Deconstruct(out string, out int, out int, out int)' and 'Person.Deconstruct(out string, out int, out int, out (int DayNumber, DayOfWeek DayOfWeek))' (CS0121) (ValueTuples)

我已经阅读了 MSDN 和 GitHub 文档,但我不清楚为什么编译器无法确定唯一适用的重载是第二个,给定左侧的内部元组模式那作业。如有任何澄清,我们将不胜感激。

要了解发生了什么,请务必记住表达式中的内容:

(string name, _, _, (_, DayOfWeek dow))

(_, DayOfWeek dow) 部分不是元组。这是第二次解构。因此,编译器无法选择仅使用第二个 Deconstruct 来满足五个参数(通过将元组解构为最后两个参数),或者从第一个参数中获取 day 参数然后尝试查找a Deconstruct on int 以满足该部分。

要查看实际效果,请注释掉第二个 Deconstruct,然后添加:

static class MyDeconstruct
{
    public static void Deconstruct(this int i, out int dayNumber, out DayOfWeek dayOfWeek) =>
        (dayNumber, dayOfWeek) = (i, (DayOfWeek)i);
}

此时,代码再次编译正常。

对元组和解构使用相同的语法会带来很多好处。正如您所发现的那样,当您将两者混合使用时它有缺点,因为编译器无法知道您希望 (_, DayOfWeek dow) 成为解构中的元组,当它是有效的解构语法时。

但是,编译器选择使用哪个 Deconstruct 的行为似乎仍然存在严重限制,即使它提供了足够的类型信息来解析表达式。它采用一种非常简单的方法来匹配元数(参数数量)。因此,如果存在两个具有相同数量参数的 Deconstruct 方法,则无法在它们之间进行选择。例如,

(string name, _, _, int day) = dh;

应该可以正常工作,因为我们已经告诉它第四个参数的类型,因此现在只有一个 Deconstruct 匹配。但它仍然抱怨无法在两者之间做出选择。 I've therefore raised an issue with the C# team 看看是否可以在该语言的未来版本中改进。