Search code examples
c#.nettuplesc#-7.0valuetuple

Is params Keyword supports with ValueTuple as a parameter C# 7.0?


I search in StackOverflow, don't find any articles or anything related to that.

For example, the following example describes array of ValueTuple

(string CategoryName, params string[] Properties)[]  MyArrayValueTupleParameter  // Compile-Time Syntax error

Note that the previous example used as a parameter. not a variable.

But only string[] works without params? Did I missed something here or is it not supported by default?

At a glance:

This Works

void ShowAppearanceCategories((string CategoryName, string[] Properties)[] VisibleCategories)
{
    foreach (var Row in PropertyGridControl.Rows)
    {
        var VisibleCategory = VisibleCategories.FirstOrDefault(x => x.CategoryName == Row.Name);
        if (VisibleCategory != default)
        {
            foreach (var ChildRow in Row.ChildRows)
            {
                if (VisibleCategory.Properties.Any(x => ChildRow.Name.Contains(x)))
                {
                    ChildRow.Visible = false;
                }
            }
        }
        else
        {
            Row.Visible = false;
        }
    }
}

This Doesn't Work

void ShowAppearanceCategories((string CategoryName, params string[] Properties)[] VisibleCategories) // Syntax-Error
{
    foreach (var Row in PropertyGridControl.Rows)
    {
        var VisibleCategory = VisibleCategories.FirstOrDefault(x => x.CategoryName == Row.Name);
        if (VisibleCategory != default)
        {
            foreach (var ChildRow in Row.ChildRows)
            {
                if (VisibleCategory.Properties.Any(x => ChildRow.Name.Contains(x)))
                {
                    ChildRow.Visible = false;
                }
            }
        }
        else
        {
            Row.Visible = false;
        }
    }
}

Solution

  • A parameter array is a very specific language feature that is only applicable to method parameters. A value tuple is a struct with variable length generic parameters and has been given a special syntax for initialization and usage (most of which are just smoke an mirrors).

    var asd = ("asd","asd");
    

    Is basically just syntactic sugar for

    ValueTuple<string, string> valueTuple = new ValueTuple<string, string>("asd", "asd");
    

    There isn't even anything overly magical about named tuples.

    var asd = (bob1 : "asd", bob2 : "asd");
    Console.WriteLine(asd.bob1);
    

    Basically converts to the following

    ValueTuple<string, string> valueTuple = new ValueTuple<string, string>("asd", "asd");
    Console.WriteLine(valueTuple.Item1);
    

    So you are essentially asking, why you can't use params in a tuple initializer syntax.

    Well, as discussed, because a value tuple initializer actually isn't a method, and as much as it looks like it, the arguments you give it are not a method parameters. Tuple syntax is it's own very special language feature. However, you do have options.

    So let's look at what you are trying to achieve. If you had a method parameter as such

    public void Test((string arg1, params string[] args) tuple)
    

    the only advantage would be you could supply a comma separated list.

    Test(("bob","args1","args2","args3"));
    

    Since we can't, your options are

    Test(("bob",new []{"args1","args2","args3"}));
    

    Or with some tomfoolery you could use an explicit operator on your own struct.

    public readonly struct Testing
    {
       public Testing(ITuple x)
       {
          CategoryName = (string) x[0];
          Properties = EnumerateTuple<string>(x).ToArray();
       }
       public Testing(string categoryName,string[] properties)
       {
          CategoryName = categoryName;
          Properties = properties;
       }
    
       private static IEnumerable<T> EnumerateTuple<T>(ITuple x)
       {
          for (var i = 1; i < x.Length; i++)
             yield return (T) x[i];
       }
       public string CategoryName { get; }
       public string[] Properties { get; }
       public static implicit operator Testing((string, string[]) x) => new Testing(x.Item1,x.Item2);
       public static implicit operator Testing(string x) => new Testing(x, Array.Empty<string>());
       public static implicit operator Testing((string, string) x) => new Testing(x);
       public static implicit operator Testing((string, string,string) x) => new Testing(x);
       public static implicit operator Testing((string, string, string, string) x) => new Testing(x);
       public static implicit operator Testing((string, string, string, string, string) x) => new Testing(x);
       public static implicit operator Testing((string, string, string, string, string, string) x) => new Testing(x);
       public static implicit operator Testing((string, string, string, string, string, string, string) x) => new Testing(x);
       public static implicit operator Testing((string, string, string, string, string, string, string, string) x) => new Testing(x);
       public static implicit operator Testing((string, string, string, string, string, string, string, string, string) x) => new Testing(x);
       public static implicit operator Testing((string, string, string, string, string, string, string, string, string, string) x) => new Testing(x);
       public static implicit operator Testing((string, string, string, string, string, string, string, string, string, string, string) x) => new Testing(x);
       public static implicit operator Testing((string, string, string, string, string, string, string, string, string, string, string, string) x) => new Testing(x);
    }
    

    Which would allow shenanigan's such as this

    public static void Test(Testing something)
    {
       Console.WriteLine(something.Category);
       Console.WriteLine(string.Join(", ", something.Properties);
    }
    
    private static void Main(string[] args)
    {
       Test("asd");
       Test(("asd"));
       Test(("asd", "args1"));
       Test(("asd", "args1", "args2"));
       Test(("asd", new []{"args1", "args2"}));
    }
    

    Note : This was only for academic purposes, I really wouldn't expect anyone to want to do this