【问题标题】:Why does Delegate.CreateDelegate allow for invalid conversions?为什么 Delegate.CreateDelegate 允许无效转换?
【发布时间】:2011-04-26 08:18:24
【问题描述】:

编辑:我提交了关于 microsoft connect:: 的错误报告 https://connect.microsoft.com/VisualStudio/feedback/details/614234/delegate-createdelegate-allows-binding-functions-with-enum-parameters-to-the-enum-base-type#details

考虑以下事情:

public static Int32 Test(Int16 @short)
{
    return @short;
}

从调用代码::

Func<Int16,Int32> test = Test; //valid method group conversion.
Func<Int16,StringComparison> test2 = Test; // doesn't compile, not valid method group conversion.
Func<Int16,StringComparison> test3 = test; // doesn't compile, invalid contra-variance.
Func<Int16,StringComparison> test4 = Delegate.CreateDelegate(typeof(Func<Int16,StringComparison>),test.Method) as Func<Int16,StringComparison>; // works fine.

为什么 Delegate.CreateDelegate 可以进行这种其他创建函数的方法都不允许的奇怪转换?更糟糕的是,说Int64Object 都失败了。我意识到StringComparison“扩展”Int32,但我认为这更像是一个编译器技巧,因为枚举扩展了Enum 类。

此外,此转换也适用于 DynamicMethod.CreateDelegate

Edit 刚刚用Nullable&lt;Int32&gt; 尝试过,它不起作用,这很令人困惑。我认为唯一的“免费”转换是将Enum 类型转换为其基础类型,但为什么呢?

请注意,这不允许您将 int 实例方法(如 GetHashCode)转换为采用 enum 类型的开放方法,这就是为什么我认为这是一个错误,因为行为不一致。

编辑: 如果我们删除 test2 和 test3 行,我们可以测试以查看方法、委托和“非法”委托是否都按预期工作。

Console.WriteLine(Test(0)); // prints 0
Console.WriteLine(test(0)); // prints 0
Console.WriteLine(test4(0)); //prints CurrentCulture

编辑: 这是我在大约 10 分钟内写的一个非常大的滥用。这为TEnum 创建了一个IEqualityComparer&lt;T&gt;,基本上抓住了它的底层类型,然后只包装Equals 和HashCode 并使用这个技巧/滥用将参数转换为TEnums,而不是底层类型。如果这是一个错误,我想知道这样我就不会尝试依赖这种行为。

class EnumComparer<TEnum> : EqualityComparer<TEnum> where TEnum : struct
{
    static Func<TEnum, TEnum, bool> s_Equals;
    static Func<TEnum, int> s_HashCode;
    static EnumComparer<TEnum> s_default;
    static EnumComparer()
    {
        if (!typeof(TEnum).IsEnum) throw new Exception("Not an enum type");
        Type underlyingType = Enum.GetUnderlyingType(typeof(TEnum));
        object equalityComparer = typeof(EqualityComparer<>).MakeGenericType(new[] { underlyingType }).GetProperty("Default").GetGetMethod().Invoke(null, null);
        s_Equals = Delegate.CreateDelegate(typeof(Func<TEnum, TEnum, bool>), equalityComparer,equalityComparer.GetType().GetMethod("Equals", new[]{underlyingType,underlyingType})) as Func<TEnum,TEnum,bool>;
        s_HashCode = Delegate.CreateDelegate(typeof(Func<TEnum, int>), equalityComparer, equalityComparer.GetType().GetMethod("GetHashCode", new[]{underlyingType})) as Func<TEnum, int>;
        s_default = new EnumComparer<TEnum>();
    }

    public static  new EnumComparer<TEnum> Default
    {
        get
        {
            return s_default;
        }
    }
    public override bool Equals(TEnum x, TEnum y)
    {
        return s_Equals(x, y);
    }

    public override int GetHashCode(TEnum obj)
    {
        return s_HashCode(obj);
    }

    private EnumComparer()
    {
    }
}

【问题讨论】:

标签: c# delegates


【解决方案1】:

这不是错误。总是可以将整数值类型转换为像 StringComparison 这样的枚举。编译器通常需要强制转换,但您在此处绕过编译器。就像在 C# 中一样,没有检查来验证整数值是否真的代表枚举值之一。

【讨论】:

  • 你确定吗?当我检查test4.Method 时,它说它的“Int32 Test(Int16)”,这没有意义,因为它完全错误。我用表达式编写了很多代码,我必须始终将Int32-&gt;enum 类型作为返回值,否则Expression&lt;TDelegate&gt;.Compile() 将抛出一个异常,说明返回类型无效。
  • 这很有意义,这是 test 的委托定义,`Func'。 Expression 类有自己的检查转换的管道,比 CLR 允许的限制更多。
猜你喜欢
  • 2015-03-12
  • 1970-01-01
  • 1970-01-01
  • 2013-04-23
  • 2014-04-18
  • 2022-11-22
  • 2011-08-20
  • 2013-07-10
  • 2014-12-07
相关资源
最近更新 更多