Skip to content

Instantly share code, notes, and snippets.

@aalmada
Last active March 19, 2021 18:31
Show Gist options
  • Select an option

  • Save aalmada/c9ff50d3a88f556c14dda1ed3fb97202 to your computer and use it in GitHub Desktop.

Select an option

Save aalmada/c9ff50d3a88f556c14dda1ed3fb97202 to your computer and use it in GitHub Desktop.
The C# 'using' block defined by an expression tree
public static Expression Using(ParameterExpression variable, Expression content)
{
return variable.Type switch
{
{IsValueType: true} => ValueTypeUsing(variable, content),
_ => ReferenceTypeUsing(variable, content)
};
static Expression ValueTypeUsing(Expression variable, Expression content)
{
#if NETCOREAPP2_1_OR_GREATER
return variable.Type.GetCustomAttribute<IsByRefLikeAttribute>() switch
{
null => ValueTypeUsing(variable, content),
_ => ByRefLikeValueTypeUsing(variable, content)
};
#else
return ValueTypeUsing(variable, content);
#endif
static Expression ValueTypeUsing(Expression variable, Expression content)
=> typeof(IDisposable).IsAssignableFrom(variable.Type) switch
{
false => ThrowMustBeImplicitlyConvertibleToIDisposable<Expression>(variable),
_ => Expression.TryFinally(
content,
Expression.Call(Expression.Convert(variable, typeof(IDisposable)), typeof(IDisposable).GetMethod("Dispose")!))
};
static Expression ByRefLikeValueTypeUsing(Expression variable, Expression content)
{
var disposeMethod = variable.Type.GetMethod("Dispose");
return disposeMethod switch
{
null => ThrowMustBeImplicitlyConvertibleToIDisposable<Expression>(variable),
_ => Expression.TryFinally(
content,
Expression.Call(Expression.Convert(variable, typeof(IDisposable)), disposeMethod))
};
}
}
static Expression ReferenceTypeUsing(Expression variable, Expression content)
=> typeof(IDisposable).IsAssignableFrom(variable.Type) switch
{
false => ThrowMustBeImplicitlyConvertibleToIDisposable<Expression>(variable),
_ => Expression.TryFinally(
content,
Expression.IfThen(
Expression.NotEqual(variable, Expression.Constant(null)),
Expression.Call(Expression.Convert(variable, typeof(IDisposable)), typeof(IDisposable).GetMethod("Dispose")!)))
};
static T ThrowMustBeImplicitlyConvertibleToIDisposable<T>(Expression variable)
=> throw new Exception($"'{variable.Type.FullName}': type used in a using statement must be implicitly convertible to 'System.IDisposable'");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment