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 equalstrue
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)