Thursday 17 July 2008

Deciding between ref and out

C# defines two keywords for parameter passing modes: ref and out. For a full discussion of why this is, see this msdn article, from where the below examples are taken. This is a summary of which to choose and why.

When to use neither

ref and out are used for methods that pass back two or more values to the caller. This is often a code smell indicative of the need for a new encapsulating struct or class. As a rule of thumb, where three or more ref’s or out’s are required, use a struct or class to encapsulate them into a single parameter instead.

When to use 'out'

Use out when the method’s focus is to provide two or more new return values, some of which may be null/empty.

In the following example, the firstName and lastName parameter values are only of interest after the call to SplitName. Neither the callee nor the caller require knowledge of their initial value, and its valid that either or both remain null/empty.

class OutExample
{
static void SplitName(string fullName, out string firstName, out string lastName)
{
// NOTE: firstName and lastName have not been assigned to yet. Their values cannot be used.
int spaceIndex = fullName.IndexOf(' ');
firstName = fullName.Substring(0, spaceIndex).Trim();
lastName = fullName.Substring(spaceIndex).Trim();
}

static void Main()
{
string fullName = "Yuan Sha";
string firstName;
string lastName;

// NOTE: firstName and lastName have not been assigned yet. Their values may not be used.
SplitName(fullName, out firstName, out lastName);
// NOTE: firstName and lastName have been assigned, because the out parameter passing mode guarantees it.

System.Console.WriteLine("First Name '{0}'. Last Name '{1}'", firstName, lastName);
}
}


When to use 'ref'

Use ref when the method’s focus is to return a value and make use of and potentially update a second value.

In the following example, the index parameter’s value is of interest before and after the call to FindNext. Both the callee and the caller require knowledge of its initial value, and its valid that it may be modified by the call to FindNext.


class RefExample
{
static object FindNext(object value, object[] data, ref int index)
{
// NOTE: index can be used here because it is a ref parameter
while (index < data.Length)
{
if (data[index].Equals(value))
{
return data[index];
}
index += 1;
}
return null;
}

static void Main()
{
object[] data = new object[] {1,2,3,4,2,3,4,5,3};

int index = 0;
// NOTE: must assign to index before passing it as a ref parameter
while (FindNext(3, data, ref index) != null)
{
// NOTE: that FindNext may have modified the value of index
System.Console.WriteLine("Found at index {0}", index);
index += 1;
}

System.Console.WriteLine("Done Find");
}
}