Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
434 views
in Technique[技术] by (71.8m points)

c# 7.0 - C# deconstruction and overloads

While investigating the new features in C# 7.x, I created the following 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;
        }
    }
}

And the following test code:

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}");
        }
    }
}

If the Person class includes only the SECOND Deconstruct overload, the code compiles and runs fine ("Dennis was born a Friday"). But as soon as the first overload is added to Person, the compiler starts complaining, the error message being:

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)

I've read the MSDN and GitHub documentation, but it is not clear to me why the compiler cannot determine that the only applicable overload is the second, given the inner tuple pattern on the left side of the assignment. Any clarification will be appreciated.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

To understand what's going on, it's important to remember that in the expression:

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

the (_, DayOfWeek dow) part is not a tuple. It's a second deconstruct. So the compiler cannot choose between just using your second Deconstruct to satisfy the five parameters (via deconstructing the tuple to the last two parameters), or taking the day parameter from the first one and then attempting to find a Deconstruct on int to satisfy that part.

To see this in action, comment out the second Deconstruct, then add:

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

At that point, the code once again compiles just fine.

Using the same syntax for tuples and deconstructs brings many advantages. As you have discovered though, it has the disadvantage when you mix the two as the compiler has no way of knowing you want (_, DayOfWeek dow) to be a tuple in your deconstruct, when it's valid deconstruct syntax.

However, there still seems to be a severe limitation to the behaviour of compiler to choose which Deconstruct to use, even when it's provided with sufficient type information to resolve the expression. It takes a very simple approach of matching the arity (number of parameters) only. So if two Deconstruct methods exist with the same number of parameters, it can't choose between them. For example,

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

ought to work just fine as we have told it the type of the fourth parameter and thus there is now only one Deconstruct that matches. Yet it still complains it can't choose between the two. I've therefore raised an issue with the C# team to see if that can be improved in a future version of the language.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...