Sunday, April 10, 2011

Utilizing pass-by-ref Parameters

I recently helped a friend of mine code an application and at some point I created a method using a pass-by-reference parameter. Apparently this is new to him. I asked several other programmer friends that I know and most of them either know about it but never use it or don't know about it. So - here is a post about it.

To illustrate the usage of pass-by-reference, I will create to a simplistic example - where we want to calculate a loan amortization: with initial loan, interest rate, and loan period - and resulting in: monthly payment and total interest over loan term

So in code, without the pass-by-reference, we have to do this somewhat in 2 steps/methods:

    decimal monthlyPayment = CalculateMonthlyPayment(initialLoan, rate, period);
    decimal totalInterest = CalculateTotalInterest(initialLoan, rate, period);
With the definitions:
public decimal CalculateMonthlyPayment(decimal initialLoan, double rate, int period) { 
        decimal result;
        // calculate monthly payment
        return result;
    }

    public decimal CalculateTotalInterest(decimal initialLoan, double rate, int period) { 
        decimal result;
        // calculate interest
        return result;
    }


While the 2 steps thing is OK, but we know that we can optimize that better since both methods use the same parameter and both methods have a lot of similarity in their calculation. So it would be better if we can combine both methods - how can can we get 2 results with 1 methods? Maybe we should return the results in an array or make a "MortgageAmortization" class? Both ideas are OK and no doubt are also easily doable. But, I think this is also the right place to use pass-by-reference parameter in simplifying and optimizing our methods.

There are 2 ways to do pass-by-reference in C#: out or ref. The difference between them is small but important: argument passed to a ref parameter must be initialized - where out param does not have to be. You can read about ref and out further here.

So, using pass-by-reference, our code becomes something like this:
decimal monthlyPayment = 0;
    decimal totalInterest = 0;
    monthlyPayment = CalculateMortgage(initialLoan, rate, period, out totalInterest);
    // or you can use ref instead of out as long as the method signatures match
    // monthlyPayment = CalculateMortgage(initialLoan, rate, period, ref totalInterest);

public decimal CalculateMortgage(decimal initialLoan, double rate, int period, out totalInterest) { 
        decimal result;

        // calculate monthly payment

        // set total interest
        // totalInterest = 12500;

        return result;
    }

2 comments:

Christopher Slee said...

I've been doing a lot of lua and that is one thing I love.. multiple return values.. its used very often in what is considered "well formed" lua

A call like this:
local success, header, items = model_characters:get( UID, AKey )

And inside:
function model_characters:get(u, a)
local path = u..filename
local exists, x = eveixutil:read(path)
if(exists) then
local h, i = translate(x, u)
for r,rows in pairs(h) do
for rname, rvalue in pairs(rows) do
if((rname=="localcachedseconds" and rvalue>os.time()) or _G.internet==false) then
return true, h, i
end
end
end
end
if(cacheAPI(u, a)) then
return self:get(u, a)
else
return false
end
end

Aaron Stemen said...

This is one of those "preference" situations. I like the more "pure" functional approach of returning an entity (struct or class) with the data. TryParse functions follow the example that you're proposing, but after doing a little reading, I agree with what I read somewhere that out parameters are never needed.

I do fall back on what I learned of optimizing SQL Server development where output parameters are more efficient than returning a single row in a recordset. So if performance is your goal, then out/ref parameters may be more efficient. I would like to see some performance numbers comparing returning an entity vs out vs ref.