当对象是 Task 时,获取 Func 的结果
2024-01-25

我目前正在使用此代码来尝试动态执行保存的Func<object>:

public async Task<object> GetFuncResult(string funcName) {
    Func<object> func = _savedFuncs[funcName];
    bool isAwaitable = func.Method.ReturnType.GetMethod(nameof(Task.GetAwaiter)) != null;
    if (!isAwaitable) return func();
    else return await ((Func<Task<object>>)func)();
}

如果有人存储一个Func<Task<object>> or a Func<[anything]>这段代码工作正常。但是如果有人存储一个Func<Task<string>>(或任务中的任何其他通用参数),它会中断。

Unable to cast object of type Func<Task<System.String>> to type Func<Task<System.Object>>

我的问题是: 如何等待结果Func<Task<Something>>此时,只需将该值返回为object?

完整测试代码:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace TestConsole
{
    class Program
    {
        static Dictionary<string, Func<object>> _savedFuncs;

        static async Task Main(string[] args)
        {
            _savedFuncs = new Dictionary<string, Func<object>>();
            Func<Task<string>> myTask = async () => { return "Test Success"; };
            _savedFuncs.Add("myFunc", myTask);
            Console.WriteLine((await GetFuncResult("myFunc")) ?? "No Value Returned");
            Console.ReadKey();
        }

        public static async Task<object> GetFuncResult(string funcName)
        {
            Func<object> func = _savedFuncs[funcName];
            bool isAwaitable = func.Method.ReturnType.GetMethod(nameof(Task.GetAwaiter)) != null;
            if (!isAwaitable) return func();
            return await ((Func<Task<object>>)func)();
        }
    }
}

我不完全清楚你的意图是什么,因为代码不清楚。您正在寻找一个GetAwaiter()方法的返回类型,但当然还有其他类型Task有这个方法的。此外,您的代码将错过通过扩展方法等待的内容。

如果您打算假设该函数返回一个任务对象(代码当前确实这样做),那么您应该只检查它而不是GetAwaiter()方法。相反,您应该只调用GetAwaiter()动态地调用方法,以便容纳该方法的任何内容。

就个人而言,如果不会经常调用此代码,我会使用dynamic, 尝试调用GetAwaiter(),如果失败(因为该方法不存在),则捕获异常并直接调用委托。如果性能很重要,您可以记住类型到等待者的状态,以便在您点击一次后可以跳过异常。请注意,使用dynamic,您将适应大多数可等待的场景(它仍然找不到扩展方法GetAwaiter()s).

这是一个例子:

private static readonly HashSet<MethodInfo> _notAwaitable = new HashSet<MethodInfo>();

public static async Task<object> GetFuncResult(string funcName)
{
    Func<object> func = _savedFuncs[funcName];
    dynamic result = func();

    if (!_notAwaitable.Contains(func.Method))
    {
        try
        {
            return await result;
        }
        catch (RuntimeBinderException) { } // not awaitable

        _notAwaitable.Add(func.Method);
    }

    return result;
}

这应该可以满足您的要求,并且还应该具有高性能。这dynamic运行时支持已经缓存了可等待场景的解决方案,并通过存储不可等待的场景MethodInfo哈希集中的实例,代码避免了遭受RuntimeBinderException对于任何给定的委托目标方法多次。

一旦代码“预热”(即以后续传递的方式调用),它就不应该成为瓶颈。

请注意,上面的实现假设您是not使用多播委托。考虑到上下文,这似乎是一个合理的假设,因为没有对可等待的多播委托的内置语言支持(或者更确切地说,它会起作用,但运行时中没有任何东西可以解决关于which可等待的正在等待)。但如果您确实需要的话,您当然可以扩展上述内容以支持多播委托。

如果您不关心支持所有可等待的场景,而只是Task基于 - 的,你可以像这样简化上面的内容:

public static async Task<object> GetFuncResult(string funcName)
{
    Func<object> func = _savedFuncs[funcName];
    object result = func();

    if (result is Task task)
    {
        await task;
        return ((dynamic)task).Result;
    }

    return result;
}

在这里,类型检查Task用于代替哈希集。再次,dynamic运行时支持将缓存Result与此方法一起使用的每种类型的任务的访问器,因此一旦预热,将与任何其他面向动态的解决方案一样执行。

最后,请注意,如果您有Func<Task>,上面的方法不起作用,因为它假设所有Task对象有一个有效的结果。有人可能会说,考虑到这种歧义,最好一开始就不要在字典中填充类似的内容。但假设这种情况令人担忧,则可以修改上述内容以考虑这种可能性:

public static async Task<object> GetFuncResult(string funcName)
{
    Func<object> func = _savedFuncs[funcName];
    object result = func();

    if (result is Task task)
    {
        Type resultType = result.GetType();

        // Some non-result task scenarios return Task<VoidTaskResult> instead
        // of a plain non-generic Task, so check for both.
        if (resultType != typeof(Task) &&
            resultType.GenericTypeArguments[0].FullName != "System.Threading.Tasks.VoidTaskResult")
        {
            await task;
            return ((dynamic)task).Result;
        }
    }

    return result;
}

不幸的是,因为在某些情况下编译器使用Task<VoidTaskResult>而不是非通用的Task类型,仅检查是不够的Task。另外,因为VoidTaskResult不是公共类型,代码必须检查类型名称是否为string值而不是typeof(Task<VoidTaskResult>)。所以,有点尴尬。但它将解决返回的东西是Task本身,而不是任务的结果。

当然,GetAwaiter()方法不存在这个问题。因此,如果这确实令人担忧,那么这将是选择GetAwaiter()方法而不是is Task方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当对象是 Task 时,获取 Func 的结果 的相关文章
  • boost::multi_index_container 复合键中的 equal_range 与比较运算符

    我正在尝试从多索引容器查询结果 其中值类型是三个元素的结构 第一个值已给出 但第二个和第三个值必须大于或小于查询参数 经过搜索后 我发现必须实现自定义密钥提取器 并且这里的一些链接建议相同 但我无法实现它 boost multi index
  • 在 Xamarin Android 中将图像从 URL 异步加载到 ImageView 中

    我有一个包含多个项目的 ListView 列表中的每个项目都应该有一个与之关联的图像 我创建了一个数组适配器来保存每个列表项并具有我希望加载的图像的 url 我正在尝试使用 Web 请求异步加载图像 并设置图像并在加载后在视图中更新它 但视
  • 如何在C++中实现模板类协变?

    是否可以以这样一种方式实现类模板 如果模板参数相关 一个对象可以转换为另一个对象 这是一个展示这个想法的例子 当然它不会编译 struct Base struct Derived Base template
  • 使用 C# 在 WinRT 中获取可用磁盘空间

    DllImport kernel32 dll SetLastError true static extern bool GetDiskFreeSpaceEx string lpDirectoryName out ulong lpFreeBy
  • C# 用数组封送结构体

    假设我有一个类似于 public struct MyStruct public float a 我想用一些自定义数组大小实例化一个这样的结构 在本例中假设为 2 然后我将其封送到字节数组中 MyStruct s new MyStruct s
  • 按字典顺序对整数数组进行排序 C++

    我想按字典顺序对一个大整数数组 例如 100 万个元素 进行排序 Example input 100 21 22 99 1 927 sorted 1 100 21 22 927 99 我用最简单的方法做到了 将所有数字转换为字符串 非常昂贵
  • 在 ASP.Net Core 2.0 中导出到 Excel

    我曾经使用下面的代码在 ASP NET MVC 中将数据导出到 Excel Response AppendHeader content disposition attachment filename ExportedHtml xls Res
  • Windows 窗体不会在调试模式下显示

    我最近升级到 VS 2012 我有一组在 VS 2010 中编码的 UI 测试 我试图在 VS 2012 中启动它们 我有一个 Windows 窗体 在开始时显示使用 AssemblyInitialize 属性运行测试 我使用此表单允许用户
  • 编译的表达式树会泄漏吗?

    根据我的理解 JIT 代码在程序运行时永远不会从内存中释放 这是否意味着重复调用 Compile 表达式树上会泄漏内存吗 这意味着仅在静态构造函数中编译表达式树或以其他方式缓存它们 这可能不那么简单 正确的 他们可能是GCed Lambda
  • 初始化变量的不同方式

    在 C 中初始化变量有多种方法 int z 3 与 int 相同z 3 Is int z z 3 same as int z z 3 您可以使用 int z z 3 Or just int z 3 Or int z 3 Or int z i
  • 更改窗口的内容 (WPF)

    我创建了一个简单的 WPF 应用程序 它有两个 Windows 用户在第一个窗口中填写一些信息 然后单击 确定 这会将他们带到第二个窗口 这工作正常 但我试图将两个窗口合并到一个窗口中 这样只是内容发生了变化 我设法找到了这个更改窗口内容时
  • 网络参考共享类

    我用 Java 编写了一些 SOAP Web 服务 在 JBoss 5 1 上运行 其中两个共享一个类 AddressTO Web 服务在我的 ApplycationServer 上正确部署 一切都很顺利 直到我尝试在我的 C 客户端中使用
  • AccessViolationException 未处理

    我正在尝试使用史蒂夫 桑德森的博客文章 http blog stevensanderson com 2010 01 28 editing a variable length list aspnet mvc 2 style 为了在我的 ASP
  • 在 URL 中发送之前对特殊字符进行百分比编码

    我需要传递特殊字符 如 等 Facebook Twitter 和此类社交网站的 URL 为此 我将这些字符替换为 URL 转义码 return valToEncode Replace 21 Replace 23 Replace 24 Rep
  • 将日期参数传递给对 MVC 操作的 ajax 调用的安全方法

    我有一个 MVC 操作 它的参数之一是DateTime如果我通过 17 07 2012 它会抛出一个异常 指出参数为空但不能有空值 但如果我通过01 07 2012它被解析为Jan 07 2012 我将日期传递给 ajax 调用DD MM
  • 已过时 - OpenCV 的错误模式

    我正在使用 OpenCV 1 进行一些图像处理 并且对 cvSetErrMode 函数 它是 CxCore 的一部分 感到困惑 OpenCV 具有三种错误模式 叶 调用错误处理程序后 程序终止 Parent 程序没有终止 但错误处理程序被调
  • 在 ASP.NET 中将事件冒泡为父级

    我已经说过 ASP NET 中的层次结构 page user control 1 user control 2 control 3 我想要做的是 当控件 3 它可以是任何类型的控件 我一般都想这样做 让用户用它做一些触发回发的事情时 它会向
  • 窗体最大化时自动缩放子控件

    有没有办法在最大化屏幕或更改分辨率时使 Windows 窗体上的所有内容自动缩放 我发现手动缩放它是正确的 但是当切换分辨率时我每次都必须更改它 this AutoScaleDimensions new System Drawing Siz
  • 如何使用 ReactiveList 以便在添加新项目时更新 UI

    我正在创建一个带有列表的 Xamarin Forms 应用程序 itemSource 是一个reactiveList 但是 向列表添加新项目不会更新 UI 这样做的正确方法是什么 列表定义 listView new ListView var
  • 将变量分配给另一个变量,并将一个变量的更改反映到另一个变量中

    是否可以将一个变量分配给另一个变量 并且当您更改第二个变量时 更改会瀑布式下降到第一个变量 像这样 int a 0 int b a b 1 现在 b 和 a 都 1 我问这个问题的原因是因为我有 4 个要跟踪的对象 并且我使用名为 curr

随机推荐