Over a million developers have joined DZone.

Easier Tuple-Like Classes in C#

DZone's Guide to

Easier Tuple-Like Classes in C#

Free Resource

A discussion during the MVP Summit prompted me to think about what would make it easier to use tuple-like classes while preserving valuable naming information of the tuple's constituents. The purpose of this exercise is to try and come up with a solution that does not require modification of existing C# syntax.

To set the scene, consider the following method:

bool ParseRequest(string request, out string operation, out int id) {
	string[] parts;
	if (request == null || (parts = request.Split(' ')).Length != 2) {
		return false;
	operation = parts[1];
	return int.TryParse(parts[0], out id);

Using this method entails the very inconvenient syntax typical for out parameters:

string request = ...;
string operation;
int id;
if (ParseRequest(request, out operation, out id)) ...

We can resort to tuples, but it makes things that much uglier on the caller's side:

Tuple<bool, string, int> ParseRequest(string request) ...

var result = ParseRequest(request);
if (result.Item1) ... //continue to use result.Item2, result.Item3

One alternative that requires syntax changes relies on providing methods with multiple return values, or at least syntax for unpacking tuples such as the following:

(success, operation, id) = ParseRequest(request);
if (success) ...

In the latter syntax, the types of success, operation, and id would be implicitly determined by the compiler based on the return type of the ParseRequest method. But again, that requires syntax changes. Here's an idea that doesn't:

struct ParseResult {
	public bool Success;
	public string Operation;
	public int Id;

ParseResult ParseRequest(string request) {
	//proceeds to return new ParseResult { ... }

This is nice, but loses many of the advantages that the Tuple class has, such as immutability. How about we combine the two?

class ParseResult : Tuple<bool, string, int> {}

That's bad for the caller -- we still have to access the Item1Item2, and Item3 properties. In other words, we need renaming:

class ParseResult : Tuple<bool, string, int> {
	public bool Success { return Item1; }
	public string Operation { return Item2; }
	public int Id { return Item3; }

Now, this is the kind of thing compilers are good at. It is tempting to go back to the drawing board and add some auxiliary syntax, maybe something like:

class ParseResult (bool success, string operation, int id) {}

The compiler could then go ahead and generate exactly the same thing as the previous snippet. If you don't like the parens before the class definition, I guess we could settle for the following:

[RenamedTuple("success", "operation", "id")]
class ParseResult : Tuple<bool, string, int> {}

This is even something you could easily feed into an AOP framework and expect it to generate the boilerplate code for you. Alternatively, if you can live with the perf hit introduced by dynamic invocation, you could have the following:

class ParseResult : DynamicTuple<bool, string, int> {
	public ParseResult() : base("success", "operation", "id") {}

Here, the DynamicTuple class can be essentially a dynamic key-value store that would provide the necessarySuccessOperation, and Id properties for the caller, as well as possibly a convenient constructor-like initialization syntax for the callee.

There are probably dozens of additional solutions that all nudge the verbosity in different directions, but nothing that can be concise without introducing additional syntax to the language. Hopefully this illustrates that it's easy to complain about the language, but not easy at all to come up with a satisfactory alternative that would be agreeable for everyone :-)


Published at DZone with permission of Sasha Goldshtein, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}