3

I am wondering is it possible to 'spread' tuple's values in a way to properly match method arguments.

For example:

public (int, object) GetTuple() {
   return (5, null);
}

public void ReceiveMultipleArguments(int a, object b) { ... }

The call of ReceiveMultipleArguments method like this:

ReceiveMultipleArguments(GetTuple());

will result in this error:

CS7036: There is no argument given that corresponds to the required formal parameter 'b' of 'Method1(int, object)'

The possible solution is to destructure tuple manually then provide each value as method argument, but is there a way to do it shorter, like spread operator that exists in javascript, for example?

2
  • A better idea in C# 9 and later is to use records instead of tuples and pass the record type to themethod Commented Sep 1, 2021 at 9:12
  • 1
    C# does not support any kind of spread operator - nor does it have any kind of built-in support for parameter vs. struct isomorphism, unfortunately. But you can hack-it with extension-methods on value-tuples - but it doesn't really solve any problems. Commented Sep 1, 2021 at 9:52

3 Answers 3

3

C# is a strongly typed language, so you cannot pass tuple (which has its own class ValueTuple class).

So, you could just define overload for the method:

public void Test()
{
    ReceiveMultipleArguments(GetTuple());
}

public (int, object) GetTuple()
{
    return (5, null);
}

public void ReceiveMultipleArguments((int a, object b) @params) => ReceiveMultipleArguments(@params.a, @params.b);
public void ReceiveMultipleArguments(int a, object b) { ... }
Sign up to request clarification or add additional context in comments.

1 Comment

When people say "tuple" today they're usually referring to struct ValueTuple<T1...> and C#'s special syntax for them - not the old-school class Tuple<T1...>.
0

You could change the signature of the method to support params of object elements. Then you could unpack the tuple into individual elements and use that as parameter.

public void Main()
{
    var tuple = GetTuple();
    var items = UnpackTuple(tuple).ToArray();

    DoSomethingWith(items);
}

public void DoSomethingWith(params object[] data)
{
    foreach (var d in data)
    {
        Console.WriteLine(d);
    }
}

public IEnumerable<object> UnpackTuple(ITuple tuple)
{
    for (var index = 0; index < tuple.Length; index++)
    {
        yield return tuple[index];
    }
}

public ITuple GetTuple()
{
    return Tuple.Create(5, "second", 2.489, 'G');
}

However, I would strongly advice you to move away from tuples if you need to move them around in your program. From experience, I have seen that this will lead to a messy code base that is hard to understand and change.

Instead, define classes for your tuples. Lets say you need to pass an object, let's say an apple, and a count for how many apples into some method. The class could be a generic class such as:

public class CountOf<T>
{
    public CountOf(T value, int count)
    {
        this.Value = value;
        this.Count = count;
    }

    public T Value { get; }

    public int Count { get; set; }
}

Or non-generic, such as:

public class CountedObject
{
    public CountedObject(object obj, int count)
    {
        this.Object = obj;
        this.Count = count;
    }

    public object Object { get; }

    public int Count { get; set; }
}

Use case:

public void Main()
{
    var apple = new Apple();
    
    var countedApples = new CountOf<Apple>(apple, 10);
    DoSomethingWith(countedApples);
    
    var countedObject = new CountedObject(apple, 10);
    DoSomethingWith(countedObject);
}

public void DoSomethingWith(CountOf<Apple> countedApples)
{
    // do something here
}

public void DoSomethingWith(CountedObject countedObject)
{
    // do something here
}

public class Apple { }

public class CountOf<T>
{
    public CountOf(T value, int count)
    {
        this.Value = value;
        this.Count = count;
    }

    public T Value { get; }

    public int Count { get; set; }
}

public class CountedObject
{
    public CountedObject(object obj, int count)
    {
        this.Object = obj;
        this.Count = count;
    }

    public object Object { get; }

    public int Count { get; set; }
}

Comments

0

maybe this help you:

static void Main(string[] args)
{
    ReceiveMultipleArguments(GetTuple());
    Console.WriteLine();
}

public static (int, object) GetTuple()
{
    return (5, null);
}

public static void ReceiveMultipleArguments((int, object) p)
{
    Console.WriteLine(p.Item1);
    Console.WriteLine(p.Item2);
}

2 Comments

Thanks, but I'd prefer not to change method signature.
@Душан you have to. It's either that or manually deconstruct. A ValueTuple<T1,T2> isn't neither a T1 nor a T2.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.