Although somewhat rare, I've had my times of needing to return multiple values from a function. The options at the time were:
- Using System.Tuple< ... > return types . They do work. But require an allocation, and the caller has to access via the uninformative
Item1
,Item2
. - Using out parameters. They do work, but not ideal (and no async support).
- Create a custom container class. Been there, done that. Don't like it.
Tuple Types and Tuple Literals
C# 7 has a better solution via Tuples. Here's a simple example for getting a US State name and its abbreviation in one call:
public (string, string) LookupUsState(long stateId) // tuple return type { ... // get from data storage return (StateName, StateAbbv); // tuple literal } // Caller example: void foo() { (name, abbv) LookupState(1); Console.WriteLine($"State: {name} ({abbv})"); }
As you can see, these are rather simple to use.
My real-world example, including Generics
At work, I needed to write a function which calls an InRule engine and return a custom object with the results. The code was simple:
public O GetRuleResponseAndResult<I, O>(I input) { string entityJson = Newtonsoft.Json.JsonConvert.SerializeObject(input); var response = ExecuteRuleSetJson(ruleApp, ruleset, entity, entityJson, returnEntity); return Newtonsoft.Json.JsonConvert.DeserializeObject<O>(response.EntityJson); }
Later we realized that the response
object on line 5 has some information we needed, including error and validation information. I really didn't want to use any of the three options above. C#7 now made it simple:
public (RuleExecutionResponse, O) GetRuleResponseAndResult<I, O>(I input) { string entityJson = Newtonsoft.Json.JsonConvert.SerializeObject(input); var response = ExecuteRuleSetJson(ruleApp, ruleset, entity, entityJson, returnEntity); O result = Newtonsoft.Json.JsonConvert.DeserializeObject<O>(response.EntityJson); return (response, result); // returning both objects } // Calling code: var (response, output) = GetRuleResponseAndResult<EnDRuleOutput, EnDInput>(input); if (response.Error == null) { // Do something with output }
Rather clean. Happy coding...