c# - Delegate.CreateDelegate 不会将返回值装箱 - 是故意的,还是疏忽?

标签 c# .net

我有一个静态方法:

public class Example
{
    //for demonstration purposes - just returns default(T)
    public static T Foo<T>() { return default(T); }
}

我需要能够使用 Type 调用它参数调用可能很多,所以我的标准模式是创建一个线程安全的委托(delegate)缓存(在 .Net 4 中使用 ConcurrentDictionary)动态调用 Foo<T>方法与正确 T .但是,如果没有缓存,代码是这样的:

static object LateFoo(Type t) 
{ 
  //creates the delegate and invokes it in one go
  return (Func<object>)Delegate.CreateDelegate( 
    typeof(Func<object>), 
    typeof(Example).GetMethod("Foo", BindingFlags.Public | BindingFlags.Static). 
      MakeGenericMethod(t))(); 
}

这不是我第一次不得不这样做 - 过去我使用表达式树来构建和编译代理来调用目标方法 - 以确保从 int 返回类型转换和装箱 ->对象(例如)被正确处理。

更新 - 有效的表达式代码示例

static object LateFoo(Type t)
{
  var method = typeof(Example)
               .GetMethod("Foo", BindingFlags.Public | BindingFlags.Static)
               .MakeGenericMethod(t); 
  //in practise I cache the delegate, invoking it freshly built or from the cache
  return Expression.Lambda<Func<IField, object>>(Expression.Convert(
    Expression.Call(method), typeof(object))).Compile()();
}

有点有趣的是,我很早就学会了使用显式 Convert 的表达式。是必需的并接受了它 - 代替这里的答案,现在可以理解为什么 .Net 框架不会自动将等价物插入。

结束更新

然而,这次我想我只需要使用 Delegate.CreateDelegate因为它充分利用了以下事实(来自 MSDN):

Similarly, the return type of a delegate is compatible with the return type of a method if the return type of the method is more restrictive than the return type of the delegate, because this guarantees that the return value of the method can be cast safely to the return type of the delegate.

现在 - 如果我通过 typeof(string)LateFoo方法,一切正常。

但是,如果我通过 typeof(int)我得到一个 ArgumentExceptionCreateDelegate 上电话、留言:Error binding to target method .没有内部异常或进一步的信息。

所以看起来,为了方法绑定(bind)的目的,object不被认为比 int 更具限制性.显然,这一定是因为装箱是一种不同于简单类型转换的操作,并且值类型不被视为与 object 协变。在 .Net 框架中;尽管在运行时存在实际类型关系。

C# 编译器似乎同意这一点(这是我可以模拟错误的最短方式,忽略代码会做什么):

public static int Foo()
{
    Func<object> f = new Func<object>(Foo);
    return 0;
}

不编译因为 Foo方法“有错误的返回类型”-给定 CreateDelegate问题,C# 只是在追随 .Net 的脚步。

在我看来,.Net 在处理协方差方面不一致 - 值类型要么是 object或者不是; & 如果不是,则不应公开 object作为基地(尽管它会让我们的生活变得更加困难)。因为它确实公开了 object作为基础(或者它只是这样做的语言吗?),那么根据逻辑,值类型应该与 object 协变。 (或者你应该说的任何一种方式)使这个委托(delegate)正确绑定(bind)。如果协方差只能通过装箱操作来实现;那么框架应该处理这个问题。

我敢说这里的答案是 CreateDelegate 并没有说它将以协变方式处理框操作,因为它只使用“cast”这个词。我还希望有关于值(value)类型和对象协变等更广泛主题的完整论文,我正在大声疾呼一个早已不复存在且已解决的主题。不过,我认为有一些我不明白或错过的东西 - 所以请指教!

如果无法回答 - 我很乐意删除。

最佳答案

如果参数和返回值可以使用表示保留转换来转换,则只能以这种方式转换委托(delegate)。

  • 引用类型只能通过这种方式转换为其他引用类型
  • 整数值可以转换为其他相同大小的整数值(相同大小的 int、uint 和 enums 是兼容的)

一些相关的博客文章:

This dichotomy motivates yet another classification scheme for conversions (†). We can divide conversions into representation-preserving conversions (B to D) and representation-changing conversions (T to U). (‡) We can think of representation-preserving conversions on reference types as those conversions which preserve the identity of the object. When you cast a B to a D, you’re not doing anything to the existing object; you’re merely verifying that it is actually the type you say it is, and moving on. The identity of the object and the bits which represent the reference stay the same. But when you cast an int to a double, the resulting bits are very different.

This is why covariant and contravariant conversions of interface and delegate types require that all varying type arguments be of reference types. To ensure that a variant reference conversion is always identity-preserving, all of the conversions involving type arguments must also be identity-preserving. The easiest way to ensure that all the non-trivial conversions on type arguments are identity-preserving is to restrict them to be reference conversions. http://blogs.msdn.com/b/ericlippert/archive/2009/03/19/representation-and-identity.aspx

"but how can a value type, like int, which is 32 bits of memory, no more, no less, possibly inherit from object? An object laid out in memory is way bigger than 32 bits; it's got a sync block and a virtual function table and all kinds of stuff in there." Apparently lots of people think that inheritance has something to do with how a value is laid out in memory. But how a value is laid out in memory is an implementation detail, not a contractual obligation of the inheritance relationship! When we say that int inherits from object, what we mean is that if object has a member -- say, ToString -- then int has that member as well. http://ericlippert.com/2011/09/19/inheritance-and-representation/

关于c# - Delegate.CreateDelegate 不会将返回值装箱 - 是故意的,还是疏忽?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8818089/

相关文章:

javascript - 我需要一个特定的十进制转换我做不到

c# - 用于检索所有记录的 Elasticsearch 搜索查询 NEST

c# - 如何将枚举绑定(bind)到 WPF 中组合框的依赖属性?

.net - 如何制作 Visual Studio 项目文件来复制间接引用?

c# - ObjectListView - 过滤的行数

.net - emum 的多个描述属性

c# - ComboBox 向上/向下箭头键在项目重新填充后发出

c# - 如何在 Windows 窗体应用程序 (C#) 中制作垂直分隔符

C#,转义双引号不能按预期工作

c# - 来自普通 SQL 的 Oracle sys_refcursor