为什么直接转换失败但"as"运算符在测试约束泛型类型时成功?

hat*_*h22 10 c# generics casting constraints .net-4.0

``编译一些使用带有类型约束的泛型的C#代码时,我遇到了一个有趣的好奇心.我写了一个快速测试用例来说明.我在Visual Studio 2010中使用.NET 4.0.

namespace TestCast
{
    public class Fruit { }

    public class Apple : Fruit { }

    public static class Test
    {
        public static void TestFruit<FruitType>(FruitType fruit) 
            where FruitType : Fruit
        {
            if (fruit is Apple)
            {
                Apple apple = (Apple)fruit;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

对Apple的强制转换失败并显示错误:"无法将类型'FruitType'转换为'TestCast.Apple'".但是,如果我更改行以使用as运算符,它编译时没有错误:

Apple apple = fruit as Apple;
Run Code Online (Sandbox Code Playgroud)

有人可以解释为什么会这样吗?

Eri*_*ert 20

在2015年10月使用这个问题作为博客文章的基础.谢谢你这个好问题!

有人可以解释为什么会这样吗?

"为什么"这些问题很难回答; 答案是"因为那是规范所说的"然后自然的问题是"为什么规范会这么说?"

所以让我让问题更加清晰:

哪些语言设计因素影响了使约束类型参数使给定的强制转换操作符非法的决定?

请考虑以下情形.你有一个基本类型的水果,派生类型Apple和香蕉,现在是重要的部分,用户定义的从Apple到香蕉的转换.

你认为这应该做什么M<Apple>

void M<T>(T t) where T : Fruit
{
    Banana b = (Banana)t;
}
Run Code Online (Sandbox Code Playgroud)

阅读代码的大多数人会说这应该调用从Apple到Banana的用户定义转换.但是C#泛型不是C++模板; 对于每个通用构造,不会从头开始重新编译该方法.相反,该方法被编译一次,并且在该编译期间,为每个可能的通用实例确定每个运算符(包括强制转换)的含义.

主体M<Apple>必须具有用户定义的转换.身体M<Banana>会有身份转换. M<Cherry>会是一个错误.在泛型方法中,我们不能有三个不同的运算符含义,因此运算符被拒绝.

相反,你要做的是:

void M<T>(T t) where T : Fruit
{
    Banana b = (Banana)(object)t;
}
Run Code Online (Sandbox Code Playgroud)

现在两次转换都很清楚.转换为对象是隐式引用转换; 转换为Banana是一种明确的参考转换.永远不会调用用户定义的转换,如果这是使用Cherry构造的,那么错误是在运行时,而不是编译时,因为它始终是对象转换时.

as运营商是不喜欢投操作; 无论给出什么类型,它总是意味着相同的事情,因为as操作员不会调用用户定义的转换.因此,它可以在演员非法的情况下使用.

  • @ hatch22:我同意你的看法.演员是一个奇怪的鸭嘴兽,既不是完全哺乳动物也不是蜥蜴.一个强制转换操作符可能意味着"编译器!这个值实际上属于那种类型,所以让我违反你的类型检查规则,如果我错了就抛出异常." 它也可以表示"编译!这个值不是那种类型,但你知道要调用哪个函数来获得该类型的等价值;调用函数".那些是*对立*,这就是为什么演员是如此奇怪. (7认同)