Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tuples vs. custom struct for async version of a method with ref/out parameter

Consider a C# API with method that has out or ref parameter, e.g.:

Person FindPerson(string name, out int searchTime);

Let's not pay attention to the fact that out and ref parameters are usually a design smell, let's say this is a legacy API and we can't change the signature of its existing methods. But we need to extend the API to support asynchronous execution (Windows Phone, WinRT apps). Here's an implementation that won't compile:

Task<Person> FindPersonAsync(string name, out int searchTime)
{
    return Task.Factory.StartNew(() => this.FindPersonAsync(name, out searchTime));
}

This implementation won't compile due to out parameter. So we have to change the API signature. One way is to change the result from task-of-person to task-of-tuple-of-person-and-int, i.e. the delegate implementation will be returning a Tuple of Person and int. The second alternative is to define a custom structure.

Tuple advantage: Using tuples gives a very formal approach that can be used to easily define async version for any API. Predictable implementation (no new names defined). Using custom structures requires inventing new types and members for each such case.

Custom struct advantage: client code using tuples will need to refer to Tuple elements using Item1 and Item2 names. This is obscure.

I haven't found any recommendations and for the time being decided to use tuples. But I wonder if there's a recommended practice for dealing with such methods when extending API with asyns support.

like image 928
Vagif Abilov Avatar asked Dec 07 '25 08:12

Vagif Abilov


1 Answers

I am not aware of any best practices or recommendations on the matter; but from my own experience Tuple makes the code ugly and meaningless - What means Item1 when you see one?

I always go with a custom class and keeping up some conventions:

  • Name of custom class ends in Result like FindPersonResult.
  • Design the class as a POCO with no special logic in constructor. That prevents any surprises when you are working with other parts of the system and other APIs (especially in serializing and deserializing).
  • Always set the initial value of collection fields/properties (Arrays, Lists, etc) to an empty one not null. That helps too in many cases.

Keep in mind these are just my personal experiences on the matter which worked seamlessly for me.

like image 185
Kaveh Shahbazian Avatar answered Dec 08 '25 22:12

Kaveh Shahbazian