如何重载函数以接受回调参数的异步和同步版本
How do I overload function to accept both async and synchronized version of callback parameter
public static T SyncVer<T>(Func<T> callback)
{
using (new LogContext("new logging context"))
{
try
{
return callback();
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
}
public static async Task<T> AsyncVer<T>(Func<Task<T>> callback)
{
using (new LogContext("new logging context"))
{
try
{
return await callback();
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
}
请考虑上面的代码。您可能会看到这两个函数中的大部分代码都是相同的。我正在寻找一种方法,通过将它们重载为一个(或者如果有办法去掉两个函数的相似部分)来将它们分组,这样我就不需要复制内容了?
我们将不胜感激。提前致谢。
我会尝试这样的事情:
public static T SyncVer<T>(Func<T> callback)
{
return AsyncVer(() => Task.FromResult(callback())).GetAwaiter().GetResult();
}
注意Task.FromResult
会分配,GetAwaiter().GetResult()
可能死锁
就我个人而言,我只会创建两个方法而不用担心它。
然而,另一种(稍微更安全的)方法是将回调包装在 ValueTask
中。 ValueTasks
如果它们同步执行,效果最好,但是它们有一些微妙的限制,永远不要等待超过一次。
假设是,这都是关于创建 awaitable 和非 awaitable 委托重载、代码重用和等待同步版本这个电话对你来说不是问题。
给定
public static async ValueTask<T> SomethingAsync<T>(Func<T> callback)
=> await SomethingAsync(() => new ValueTask<T>(callback()));
public static async ValueTask<T> SomethingAsync<T>(Func<Task<T>> callback)
=> await SomethingAsync(() => new ValueTask<T>(callback()));
public static async ValueTask<T> SomethingAsync<T>(Func<ValueTask<T>> callback)
{
using (new LogContext("new logging context"))
{
try
{
return await callback();
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
}
用法
public static string DoSomething()
{
Console.WriteLine("execute sync");
return "sync result";
}
public static async Task<string> DoSomethingAsync()
{
Console.WriteLine("Execute async");
await Task.Delay(100);
return "async result";
}
...
Console.WriteLine(await SomethingAsync(DoSomething));
Console.WriteLine(await SomethingAsync(DoSomethingAsync));
输出
Create
execute sync
Dispose
sync result
Create
Execute async
Dispose
async result
要提高效率,您可以省略包装器
例子
public static ValueTask<T> SomethingAsync<T>(Func<T> callback)
{
try
{
return SomethingAsync(() => new ValueTask<T>(callback()));
}
catch (Exception e)
{
return ValueTask.FromException<T>(e);
}
}
public static ValueTask<T> SomethingAsync<T>(Func<Task<T>> callback)
{
try
{
return SomethingAsync(() => new ValueTask<T>(callback()));
}
catch (Exception e)
{
return ValueTask.FromException<T>(e);
}
}
注意 : ValueTask.FromException
仅适用于 .NET 5.0+
这种方法的好处:
- 此方法的同步版本分配较少。
- 不可能出现死锁
- 它不会阻塞异步方法
- 它给你一个价值任务超载
缺点是
- 您需要将异步任务包装在ValueTask中,尽管它只是一个堆栈分配[=81] =]
- 您需要为每个方法创建两个重载(总共三个签名)
- 两个版本都不能等待两次。
- 同步版本在创建状态机时稍慢
注意:我个人从来不需要这样做,我会创建两个方法 ¯\_(ツ)_/¯
。
其他资源
public static T SyncVer<T>(Func<T> callback)
{
using (new LogContext("new logging context"))
{
try
{
return callback();
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
}
public static async Task<T> AsyncVer<T>(Func<Task<T>> callback)
{
using (new LogContext("new logging context"))
{
try
{
return await callback();
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
}
请考虑上面的代码。您可能会看到这两个函数中的大部分代码都是相同的。我正在寻找一种方法,通过将它们重载为一个(或者如果有办法去掉两个函数的相似部分)来将它们分组,这样我就不需要复制内容了?
我们将不胜感激。提前致谢。
我会尝试这样的事情:
public static T SyncVer<T>(Func<T> callback)
{
return AsyncVer(() => Task.FromResult(callback())).GetAwaiter().GetResult();
}
注意Task.FromResult
会分配,GetAwaiter().GetResult()
可能死锁
就我个人而言,我只会创建两个方法而不用担心它。
然而,另一种(稍微更安全的)方法是将回调包装在 ValueTask
中。 ValueTasks
如果它们同步执行,效果最好,但是它们有一些微妙的限制,永远不要等待超过一次。
假设是,这都是关于创建 awaitable 和非 awaitable 委托重载、代码重用和等待同步版本这个电话对你来说不是问题。
给定
public static async ValueTask<T> SomethingAsync<T>(Func<T> callback)
=> await SomethingAsync(() => new ValueTask<T>(callback()));
public static async ValueTask<T> SomethingAsync<T>(Func<Task<T>> callback)
=> await SomethingAsync(() => new ValueTask<T>(callback()));
public static async ValueTask<T> SomethingAsync<T>(Func<ValueTask<T>> callback)
{
using (new LogContext("new logging context"))
{
try
{
return await callback();
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
}
用法
public static string DoSomething()
{
Console.WriteLine("execute sync");
return "sync result";
}
public static async Task<string> DoSomethingAsync()
{
Console.WriteLine("Execute async");
await Task.Delay(100);
return "async result";
}
...
Console.WriteLine(await SomethingAsync(DoSomething));
Console.WriteLine(await SomethingAsync(DoSomethingAsync));
输出
Create
execute sync
Dispose
sync result
Create
Execute async
Dispose
async result
要提高效率,您可以省略包装器
例子
public static ValueTask<T> SomethingAsync<T>(Func<T> callback)
{
try
{
return SomethingAsync(() => new ValueTask<T>(callback()));
}
catch (Exception e)
{
return ValueTask.FromException<T>(e);
}
}
public static ValueTask<T> SomethingAsync<T>(Func<Task<T>> callback)
{
try
{
return SomethingAsync(() => new ValueTask<T>(callback()));
}
catch (Exception e)
{
return ValueTask.FromException<T>(e);
}
}
注意 : ValueTask.FromException
仅适用于 .NET 5.0+
这种方法的好处:
- 此方法的同步版本分配较少。
- 不可能出现死锁
- 它不会阻塞异步方法
- 它给你一个价值任务超载
缺点是
- 您需要将异步任务包装在ValueTask中,尽管它只是一个堆栈分配[=81] =]
- 您需要为每个方法创建两个重载(总共三个签名)
- 两个版本都不能等待两次。
- 同步版本在创建状态机时稍慢
注意:我个人从来不需要这样做,我会创建两个方法 ¯\_(ツ)_/¯
。
其他资源