Delegates as Adapters C# : More covariance
--
this is an extension of the previous post , C# Covarinace and what has to do with Functional C#.
We saw how to to create an adapter to force covarinace in our types. I will demonstrate an alternative way to write the exact same code, by using delegates. The purpose of this is twofold. 1) just deepen the understunding of delegates 2) just style points.
We can all togeather replace the two types
public interface IFuncTask<out T>
{
T GetResult();
} public class FuncTaskAdapter<T> : IFuncTask<T>
{
public FuncTaskAdapter(Func<Task<T>> funcTask) =>
this.funcTask = funcTask; public T GetResult() => funcTask().Result;
}
with a single delegate
public delegate T FuncTask<out T>();
this is essentially the signaure of the T GetResult()
method of the interface, this makes sense because a c# delegate is equivalent with a interface with a single method.
we can now create an extension method to convert from Func<Task<T>> to this delegate
static FuncTask<T> ToFuncTask<T>(this Func<Task<T>> @this) =>
new FuncTask<T>(
()=>@this().GetAwaiter().GetResult()
);
and then we can chain stuff on the delegate as usual eg:
static T GetOrThrowIfNull<T>(this FuncTask<T> @this, string message)
{
var value = @this(); // this might also throw exception from Task.
return value ?? throw new Exception(message);
}
we can now write using covarinace
Func<Task<SomeResult>> GetSomethingLazyAsync() => () => Task.FromResult(new SomeResult());
//ok this is covariant on T
FuncTask<IWithResult> lazyResult = GetSomethingLazyAsync().ToFuncTask();
SomeResult result = GetSomethingLazyAsync()
.ToFuncTask()
.GetOrThrowIfNull("failed to get");
the source
https://gist.github.com/dimitris-papadimitriou-chr/9cd3403d42ce1ef6da593d0be5fc0cdc