Better Naming in Tuple Classes Than "Item1", "Item2"

Better naming in Tuple classes than Item1, Item2

In C# 7.0 (Visual Studio 2017) there is a new construction to do that:

(string first, string middle, string last) LookupName(long id)

How to name tuple properties?

You need to declare a helper class to do so.

public class MyResult
{
public string Name { get; set; }
public string Age { get; set; }
}

What you're trying to return is an anonymous type. As the name suggests you don't know what its name is, so you can't declare your method to return it.

Anonymous Types (C# Programming Guide)

You cannot declare a field, a property, an event, or the return type
of a method as having an anonymous type.
Similarly, you cannot declare
a formal parameter of a method, property, constructor, or indexer as
having an anonymous type. To pass an anonymous type, or a collection
that contains anonymous types, as an argument to a method, you can
declare the parameter as type object. However, doing this defeats the
purpose of strong typing. If you must store query results or pass them
outside the method boundary, consider using an ordinary named struct
or class instead of an anonymous type.

Update

C#7 introduces Tuple support built into the language and it comes with named tuples

(string name, int age) methodTuple()
{
(...)
}

Read more on learn.microsoft.com: https://learn.microsoft.com/en-us/dotnet/articles/csharp/csharp-7#tuples

Is it possible to rename return(item1, item2, item3, item4, item5) to something different?

Short answer to your question

Create your own type, with the properties you want, and return that.

public class Result
{
public bool FunctionSuccessful { get; set; }
public string Errorline1 { get; set; }
public string Errorline2 { get; set; }
public string Errorline3 { get; set; }
public string Errorline4 { get; set; }
}

and:

return new Result
{
FunctionSuccessful = functionsuccessful,
Errorline1 = errorline1,
Errorline2 = errorline2,
Errorline3 = errorline3,
Errorline4 = errorline4
};

Why does it happen?

I played around with this for a little bit, as I can reproduce your problem, and I found a few interesting things.

Firstly, the reason it's doing this is because the Azure Functions runtime has a dependency on Newtonsoft.Json, and it's that package which is serialising the response. Furthermore, if you have a read of the tuple documentation you'll see:

The default names of tuple fields are Item1, Item2, Item3 and so on. You can always use the default name of a field, even when a field name is specified explicitly or inferred

Relying on those default names presumably makes supporting tuples much simpler for libraries. If you have a read of this issue on the Newtonsoft.Json repo, you'll see a discussion whereby people are skeptical of the cost of implementing named tuple fields, as they don't view it as worth the effort.

So, to be clear, I can reproduce your problem in a simple console application:

using Newtonsoft.Json;

static void Main(string[] args)
{
var functionsuccessful = true;
var errorline1 = "error 1";
var errorline2 = "error 2";
var errorline3 = "error 3";
var errorline4 = "error 4";

var result = (FunctionSuccessful: functionsuccessful,
Errorline1: errorline1,
Errorline2: errorline2,
Errorline3: errorline3,
Errorline4: errorline4);
var serialised = JsonConvert.SerializeObject(result);

Console.WriteLine(serialised);
}

which produces the output of:

{
"item1": true,
"item2": "error 1",
"item3": "error 2",
"item4": "error 3",
"item5": "error 4"
}

What I did find interesting though, is serialising with System.Text.Json instead:

serialised = System.Text.Json.JsonSerializer.Serialize(result);
Console.WriteLine(serialised);

produces:

{}

That I can't explain. I'm hesitant to call it a bug, but I definitely didn't expect that.

Edit: After looking into Seabizkit's comment, ValueTuple isn't yet supported by System.Text.Json, as confirmed by this GitHub issue and should be added for .NET 5.

Name ValueTuple properties when creating with new

No, you can't. The ValueTuple types are actually independent of the named field support in C#. The latter works more like named properties for anonymous types. That is, the compiler analyzes the code and generates aliases to the appropriate members according to your declarations and usages. It is through the assignment that the compiler learns the names of the fields. Since the basic constructor syntax doesn't provide a mechanism to name the fields, you can't use that to directly generate a tuple with named fields.

Of course, there are ways you can re-interpret the value returned from the constructor syntax, to assign names to that returned value. I'm assuming you're aware of that approach and are looking for something more direct.

As an example of what I mean by "re-interpret", you could do something like this:

static (int value, string text) ConvertToNamed((int, string) t) => t;

then this would name the fields, in a new variable:

var t1 = new ValueTuple<int, string>(21, "hello");
var t2 = ConvertToNamed(t1);

The variable t1 is stuck with Item1 and Item2. But the compiler will implicitly generate the desired names for the variable t2.

Maybe a better example is one where you don't require the additional method:

(int value, string text) t = new ValueTuple<int, string>(21, "hello");

Again, you're not really naming the fields in the constructor syntax, but they are reinterpreted by the local variable declaration.

This is probably not a serious limitation. In a scenario where there's a desire to have a persistent, easily-assigned name, it's probably better to declare a user-defined type than to use the tuple syntax anyway. You can write deconstructors for user-defined types as well, and declaring types like that means the names are first-class citizens when it comes to reflection, dynamic, etc.

C# ValueTuple properties naming

There are two different "kinds" of tuples involved here: the .NET ValueTuple<...> types, and C# tuple types. C# tuple types can have element names; ValueTuple<...> types can't. (There's simply nowhere in the type system for that to live in a normal way.)

At execution time, a value is only aware of its .NET type. For example, if you use:

(string x, int y) tuple1 = ("hello", 10);
(string a, int b) tuple2 = ("hello", 10);
DoSomethingWithValue(tuple1);
DoSomethingWithValue(tuple2);
...
void DoSomethingWithValue(object value) { ... }

then DoSomethingWithValue wouldn't be able to distinguish between the two tuple values at all. The information is only available at compile-time.

Information about element names for things like parameters and return types is propagated via attributes that the C# compiler consumes. So for example, a method declared like this:

public (string name, int score) GetFoo()

is compiled in IL as if it had been declared like this:

[TupleElementNames(new string[] { "name", "score" }]
public ValueTuple<string, int> GetFoo()

The C# language defines appropriate conversions between the .NET types and the C# tuple types to make it as seamless as possible.

In terms of how to use tuples, I'd use the C# tuple types as far as you can. Naming the tuple elements makes a huge difference in usability.

When to use: Tuple vs Class in C# 7.0

As this answer is causing some confusion amongst some folk here, I should clarify that - as per the question - all references to "tuple" here refer to the ValueTuple type and new tuple syntactic sugar features of C# 7 and in no way refer to the old System.Tuple reference types.

So now I am wondering, when Should I use tuples and when Should I create a class in c# 7.0?

Only you can really answer that question as it really depends on your code.

However, there are guidelines and rules you can follow in guiding you in choosing between them:

Tuples are values, so are copied by value, rather than by reference.

Most of the time, this should not be an issue. However, if you are passing around tuples of large structs, this might have an impact on performance. Ref locals/returns can be used to work around these performance issues, though.

Additionally, because they are values, modifying a copy remotely will not change the original copy. This is a good thing, but could catch some folk out.

Tuple element names are not persisted

The names given to elements are used by the compiler and (in most cases) are not available at run-time. This means that reflection cannot be used to discover their names; they cannot be accessed dynamically and they cannot be used in razor views.

Also this is an important consideration with APIs. A tuple returned from a method is the exception to the rule regarding after-compilation name discoverability. The compiler adds attributes to the method that hold information on the tuple names. This means you can safely return a tuple from a public method in one assembly and access its names in another.

Tuples are lightweight

Tuples are much simpler to write than types as they are less verbose and the declaration can be "inlined" (ie declared at the point of use). This works well when declaring a method that returns multiple values, for example.

However, because they are declared at the point of use, if you have MethodA that calls MethodB that calls MethodC and each returns a tuple, you'll need to redefine the tuple at every stage. There isn't (yet) a way of creating an alias of a tuple and re-using it across multiple methods.

Just use common sense

For any situation where you might consider using a tuple: simply ask yourself the question: "will a tuple simplify the code here". If the answer is "yes", then use one. And that ultimately is the primary consideration over whether to use a tuple or a custom class.



Related Topics



Leave a reply



Submit