Saturday, March 12, 2011

Function returning multiple values in C#

I always like the Python feature allowing functions to return multiple values. Behind the scene it is just about returning a list. But the Python syntax provides the perfect amount of syntactics sugar.
def ComputeValues():
    return 1, 2.0, "Hello"

a, b, c = ComputeValues()

print a, b, c
JavaScript also provides a interesting and different solution, may be not as good as Python, by returning an instance.
function ComputeValues(){

    return { returnValue:1, amount:2.0, message:"Hello" };
}
var r = ComputeValues();
print("returnValue:"    + r.returnValue);
print("amount:"         + r.amount);
print("message:"        + r.message);
In C#, I hate declaring ref and out parameters, they require more line of code, make code more difficult to read and create side effects (functional programming).

I came up with one solution with .NET 3.5 3 months ago, which I never liked. While working on the post Nice syntax to read JSon in C#, which is based on new C# 4.0 class DynamicObject, I saw a solution. The DynamicObject allows to implement among other things what is called in Ruby method_missing and in Python getattr().

I came up with different syntaxes in the past weeks, including one using an infinite list of optional parameters, but I finally settled with passing an anonymous type.

Here a sample:
// Here is a function returning 3 values
static dynamic ComputeValues() {
                        
    return DS.Values( new { a=1, b=2.0, c="Hello" } );
}
// Here is how to use the results
static void MultiValuesSample() {
                        
    var values = ComputeValues();
    Console.Write(values.a);
    Console.Write(values.b);
    Console.Write(values.c);
}
And there is no need to cast values
static void MultiValuesSample() {
                        
    var values = ComputeValues();
    int a      = values.a;
    double b   = values.b;
    string c   = values.c;
}
Wait a minute why not use this syntax?
// Here is a function returning 3 values

static dynamic ComputeValues() {
                        
    return new { a=1, b=2.0, c="Hello" };
}
If the caller and callee are located in different assembly, the anonymous type will not be accessible by the caller because an anonymous type is defined as internal. The function DS.Values() return an instance of the class DynamicSugar.MultiValues which takes care of the problem.

Why not use the Expando Object? The Expando Object works but does not allow to defines the values in one expression. The Expando Object would require one statement per name/value.

The MultiValues class is part of my library Dynamic Sugar # available soon on codeplex.com and as
a NuGet package.

No comments:

Post a Comment