Trying to map "similar looking" tuples returned from multiple functions to one variable

Viewed 39

Say there is a library with code of this form:

 public class SomeLib
{
    public (bool WasOk, string Message) DoCheck(string fileName)
    {
        // do something here to check the file. 
        return (true, "all good");
    }

    public (bool WasOk, string Message) DoCheck(byte[] fileBytes)
    {
        // do something here to check the file as bytes 
        return (true, "all good");
    }
}

The code which uses this library looks of this form:

    int option = 1;

    SomeLib lib = new SomeLib();

    if (option == 0)
    {
        var res1 = lib.DoCheck("hello");
        MessageBox.Show(res1.WasOk + res1.Message);
    }
    else
    {
        var res2 = lib.DoCheck(new byte[] { });
        MessageBox.Show(res2.WasOk + res2.Message);
    }

What I was hoping to do, was to store the returned value from the two calls to DoCheck, to a common variable. e.g.:

    int option = 1;

    SomeLib lib = new SomeLib();

    var res; // doesn't compile, obviously

    if (option == 0)
    {
        res = lib.DoCheck("hello");                
    }
    else
    {
        res = lib.DoCheck(new byte[] { });
    }

    MessageBox.Show(res.WasOk + res.Message);  // act on the result in the same way for both branches. 

I hope this makes sense. Is what I am trying to do possible? Let's say that the code in the library cannot be changed.

2 Answers

var res doesn't compile because it doesn't have any type information. If you're making a statement that declares a variable the compiler has to be able to work out the type somehow, either from the left or the right side:

StringBuilder sb = new StringBuilder(); //both sides, available from the dawn of time
var sb = new StringBuilder();           //for a long time
StringBuilder sb = new();               //more recently

If there isn't a right side then the left side has to have the type info:

StringBuilder sb;

Thus for your case you need to put the type info on the left. You can even rename the members (but you don't have to)

(bool AllGood, string Note) res;

(bool WasOk, string Message) res;

(bool, string) res;  //access the bool in res.Item1, string in res.Item2

You could do it in a one with a ternary:

var res = (option == 0) ? lib.DoCheck(hello) : lib.DoCheck(new byte[] { });

Both your methods return tuples with the same named members so your res will have res.WasOk and res.Message so it's compiled as if it's this:

(bool WasOk, string Message) res = ...

If your tuples coming out your methods had different names it would still compile as if it was this:

(bool, string) res = ...

And you could still access the data via Item1 and Item2


If you're doing this ternary approach then you could deconstruct the tuple too, which also allows you to rename:

var(allGood, note) = ... ? ... : ... ;

MessageBox.Show(note);

Instead of one tuple variable where you say res.Whatever, this has created two variables like as if you'd done this:

var res = ... ? ... : ... ;
var allGood = res.WasOk;
var note = res.Message;

Plenty of options for dealing with tuples..

Implicitly typed locals must be initialized

An implicitly typed local variable must be initialized with a value at the same time that it is declared. here

To correct this error

Assign a value to the variable or else give it an explicit type.

(bool AllGood, string Note) res;
Related