Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it better to use out for multiple output values or return a combined value type?

For instance, along the lines of:

public bool Intersect (Ray ray, out float distance, out Vector3 normal)
{

}

vs

public IntersectResult Intersect (Ray ray)
{

}

public class IntersectResult
{
    public bool Intersects {get;set;}
    public float Distance {get;set;}
    public Vector3 Normal {get;set;}
}

Which is better both for clarity, ease of use, and most importantly performance.

like image 624
Joan Venge Avatar asked Mar 10 '11 21:03

Joan Venge


2 Answers

I would use a combined type and I'll tell you why: because computation of a value should return the value, not mutate a bunch of variables. Mutating a bunch of variables doesn't scale once you need more than one of them mutated. Suppose you want a thousand of these things:

IEnumerable<Ray> rays = GetAThousandRays();
var intersections = from ray in rays 
                    where Intersect(ray, out distance, out normal)
                    orderby distance ...

Executing the query is now repeatedly mutating the same two variables. You're ordering based on a value that is being mutated. This is a mess. Don't make queries that mutate things; it is very confusing.

What you want is:

var intersections = from ray in rays 
                    let intersection = Intersect(ray)
                    where intersection.Intersects
                    orderby intersection.Distance ...

No mutation; manipulate a sequence of values as values not as variables.

I also would be inclined to maybe get rid of that Boolean flag, and make the value an immutable struct:

// returns null if there is no intersection
Intersection? Intersect(Ray ray) { ... }

struct Intersection 
{
    public double Distance { get; private set; }
    public Vector3 Normal { get; private set; }
    public Intersection(double distance, Vector3 normal) : this()
    {
        this.Normal = normal;
        this.Distance = distance;
    }
} 
like image 197
Eric Lippert Avatar answered Oct 29 '22 07:10

Eric Lippert


I would use a combined type.

With an object you can attach behaviour, and return an arbitrarily complex object. You may wish to refactor your method in the future, and change the return values. By wrapping them in a return object and adding behaviour to that object, this refactoring can become largely transparent.

It's tempting to use tuples and the like. However the refactoring effort becomes a headache after a while (I'm speaking from experience here, having just made this mistake again)

like image 28
Brian Agnew Avatar answered Oct 29 '22 05:10

Brian Agnew