【问题标题】:Invoke a Func object using reflection and a Type that's known at runtime使用反射和运行时已知的类型调用 Func 对象
【发布时间】:2018-07-22 23:52:47
【问题描述】:

我一直在努力解决这种情况。我在 SO 上浏览过的各种类似(但不完全相同)的问题并没有完全引导我到我想要的地方。

我有一个Type 和一个Func<string, (bool, T)> 类型的对象,其中T 匹配Type。我希望能够通过反射或其他方式调用它的Invoke 方法。

我在运行时有一些Type

Type type;

我在内存中有Func,但作为object

object func;

func 的底层类型是我之前描述的。例如,如果type 代表DateTime,那么func 的基础类型将是:

Func<string, (bool, DateTime)>

但是type 在运行时可以是任何东西,所以如果它是一个int,那么func 现在将是一个

Func<string, (bool, int)>

如何使用在运行时具有正确类型这一事实来调用我的函数?我假设我必须使用某种反射,但对它的理解还不够好,无法开始。

非常感谢任何帮助。

总的来说,我对反射很陌生,但我发现它非常有趣,而且对于某些事情来说非常困难。如果您有任何关于阅读的建议或任何其他关于如何更好地学习反思的建议,那也太好了!

编辑/更新经过深思熟虑:

使用dynamic 并以这种方式调用 Invoke 会更好吗?看起来它可以正常工作,但随之而来的问题是,为什么你会经历更困难或更长时间的设置反射来调用某些东西的过程,而不是简单的动态调用。

无论如何,我仍然想知道如何使用反射来工作,看看会很有趣

【问题讨论】:

  • 您必须使用两次反射,一次调用方法,一次(技术上两次)从元组中提取值。您可以按类型“缓存”所有这些反射,但不清楚您想如何使用(bool, int)(因为您想使用它,对吗?)。通过使用动态,您无法“缓存”反射,因此每次都会产生发现和动态调用的全部成本。通过使用Tuple&lt;&gt; 而不是ValueTuple&lt;&gt;,您可以跳过方法调用的反射,因为Func&lt;string, object&gt; 将接受任何Func&lt;string, Tuple&lt;*&gt;&gt;
  • 其他解决方案是使用Action&lt;string, out bool, out something&gt; 之类的签名(稍微复杂一点,但我的意思应该很清楚)...现在您需要使用单个反射来调用方法。所有这些解决方案都是可能的......但你必须知道你想要哪一个。
  • I 将使用的解决方案是将方法的签名更改为Func&lt;string, (bool, object)&gt;。该方法对第二个返回值进行装箱。没有反射,简单的代码。最后,返回 (bool, TSomething) 的方法已经是“自定义”的东西,所以我认为将其更改为 (bool, object) 以简化代码没有任何问题。

标签: c# casting func


【解决方案1】:

问题是错误的并且是“人为的”。是的,您可以使用反射,是的,您可以使用动态...但是“正确”的做法是将签名更改为:

public (bool, object) SomeMethod(string par)

代表:

Func<string, (bool, object)>

public bool SomeMethod(string par, out object obj)

委托(注意不能使用Func&lt;,&gt;/Action&lt;,&gt;):

public delegate bool MyDelegate(string par, out object obj)

使用此签名,您不需要反射来调用方法,也不需要反射来解析返回。您的返回值/outobject 中的方法装箱。性能还是比使用反射好。

其他半好的候选人可能是:

Tuple<bool, type> SomeMethod(string par)

(其中type 是您的更改类型)

代表:

Func<string, object>

Func<string, dynamic>

通过使用Tuple&lt;bool, type&gt;,您可以获得一些好处:Tuple&lt;&gt; 是一个引用类型,并且委托在返回类型中是逆变的,因此您可以将它们“统一”为object/dynamicdynamicobject 向 C# 编译器提供一些信息,表明程序员将尝试对 object 做“坏事”)

现在您必须使用反射/dynamic 来访问Tuple&lt;&gt;.Item1Tuple&lt;&gt;.Item2

使用反射和使用dynamic 的区别在于,通过使用反射,您可以通过type 部分反射“缓存”,因此可以加快以后使用type 方法的速度,而使用dynamic 这不会'不会发生。

【讨论】:

  • 看起来将其更改为使用Func&lt;string, (bool, object)&gt; 是最有意义的。奇怪的是,一开始我就是这样拥有它的,但当时拥有一种对象对我来说感觉很奇怪。我现在意识到,我一直在尝试依赖反射和动态的其他最近的东西甚至更奇怪。感谢您的详细回复/解释,帮助我更好地理解它!
  • 嘿 xanatos,我仍然遇到了一个问题,我无法完全按照自己的意愿进行操作。我在 github 上有一个具体的例子,我可以给你看。如果您想快速浏览一下,请给我发送电子邮件至 leesisaiah@gmail.com。如果没有,我完全理解!顺便说一句,我不想​​直接在这里发布链接,因为从历史的角度来看,它的价值会及时消失。我希望当前的代码很快就会改变。谢谢!
  • @IsaiahLee 很遗憾我不能...我没有太多空闲时间(我一周左右要去度假,所以我必须完成一些事情)
【解决方案2】:

如果你知道你的函数的类型,比如 Func,你可以像下面这样直接转换它

 private static void InvokeFunctionDynamically<T>(T data)
    {            

        //1. Define the delegate 
        Func<string, bool, T> genericFunction = (name, isEnabled) =>
        {
            if(isEnabled)
            {
                return data;
            }

            return default(T);
        };

        // 2. demonstrate that the delegate is retrieved at the run time as an object
        object runtimeObject = genericFunction;


        //3. Cast it directly to the delegate type
        var result =((Func<string, bool, T>) runtimeObject)("hello", true);

        Console.WriteLine($"Result {result}");
    }

结果如下:

【讨论】:

  • 您好,Toan,感谢您的回复。我使用的 Func 签名实际上是 Func 其中返回类型是一个元组
  • @IsaiahLee 是元组还是 ValueTuple?因为您在问题中的符号是针对 ValueTuple 的。
  • @xanatos 这是一个 ValueTuple,抱歉不清楚。无论如何,Toan 的解决方案不起作用,因为我不知道运行时 T 是什么。我通过实际的 Type 对象知道类型
猜你喜欢
  • 2013-05-04
  • 1970-01-01
  • 2020-08-23
  • 2011-03-30
  • 2012-01-13
  • 2011-02-28
  • 2015-11-01
  • 2021-06-22
  • 1970-01-01
相关资源
最近更新 更多