Try Do

The common way to write performance critical methods, that may fail, is obsolete. It is time to make some changes!

##Tester-Doer pattern The common way to handle errors in .NET is to use exceptions. But there is a concern related to exceptions: if code fails to often, the performance of the module may become unacceptable. The common solution for the performance critical methods, in which errors may occur, is to use the <a href=https://msdn.microsoft.com/en-us/library/ms229009(v=vs.110).aspx>Tester-Doer Pattern</a>. This pattern describes the following rules the method should follow:

  • do not throw exceptions
  • use Try prefix on the name
  • return a Boolean value which equals true if method completes successfully, false otherwise
  • use out parameter that will contain the actual result of the method if method completes successfully, default value otherwise

Unfortunately the Tester-Doer pattern has several issues:

#####Issue #1 The implicitly captured closure might occur. The following code will cause variable temp to be implicitly captured by the linq clause:

public static IEnumerable<Int32> ConvertToInt(IEnumerable<String> lines)
{
	Int32 temp = 0;

	return from
				line in lines
			where
				Int32.TryParse(line, out temp)
			select
				temp;
}

#####Issue #2 The out parameter cannot be used with async methods The following code will cause the error: error CS1988: Async methods cannot have ref or out parameters

public static async Task<Boolean> TryConnectAsync(String address, out Int32 result)

##Try-Do pattern Considering issues described above the pattern should be improved in the following way:

  • do not throw exceptions
  • use Try prefix on the name
  • return an instance of value type that implements ITryResult<T>
/// <summary>
/// Provides an interface for types which encapsulates the results of the methods,
/// that implements Try-Do pattern.
/// </summary>
/// <typeparam name="T">The type of the data returned by a Try-Do method.</typeparam>
public interface ITryResult<out T>
{
	/// <summary>
	/// Gets an instance of <typeparamref name="T"/>
	/// if the method has completed successfully, default value otherwise.
	/// </summary>
	T Result
	{
		get;
	}
	
	/// <summary>
	/// Gets a <see cref="Boolean"/> value,
	/// that indicates whether the method has completed successfully.
	/// </summary>
	Boolean Success
	{
		get;
	}
}

The ITryResult<T> interface provides an extensible way to satisfy any type of methods that follows Try-Do pattern.

Now lets see how issues will be violated by using Try-Do pattern:

#####Issue #1 The following code does not cause the implicit capture of the variables:

public static IEnumerable<Int32> ConvertToInt(IEnumerable<String> lines)
{
	return from
				line in lines
			let
            	parseResult = SomeClass.TryParse(line)
			where
            	parseResult.Sucess
			select
				parseResult.Result;
}

#####Issue #2 The async method with following declaration compiles and works:

public static async Task<ITryResult<Int32>> TryConnectAsync(String address)

Written on April 1, 2015