繁体   English   中英

为什么在测试受约束的泛型类型时直接强制转换失败但“as”运算符成功?

[英]Why does a direct cast fail but the “as” operator succeed when testing a constrained generic type?

``在编译一些使用具有类型约束的泛型的 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;
            }
        }
    }
}

转换到 Apple 失败并显示错误:“无法将类型‘FruitType’转换为‘TestCast.Apple’”。 但是,如果我更改该行以使用as运算符,则编译时不会出错:

Apple apple = fruit as Apple;

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

我用这个问题作为2015 年 10 月一篇博客文章的基础。 谢谢你的好问题!

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

“为什么”的问题很难回答; 答案是“因为规范是这么说的”,然后自然的问题是“规范为什么这么说?”

所以让我把问题说得更清楚:

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

考虑以下场景。 您有一个基本类型 Fruit、派生类型 Apple 和 Banana,现在是重要的部分,用户定义的从 Apple 到 Banana 的转换。

当被称为M<Apple>时,您认为这应该做什么?

void M<T>(T t) where T : Fruit
{
    Banana b = (Banana)t;
}

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

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

相反,您必须做的是:

void M<T>(T t) where T : Fruit
{
    Banana b = (Banana)(object)t;
}

现在这两个转换都清楚了。 到对象的转换是隐式引用转换; 到 Banana 的转换是一个显式的引用转换。 用户定义的转换永远不会被调用,如果这是用 Cherry 构造的,那么错误发生在运行时,而不是编译时,就像对象转换时一样。

as运算符与 cast 运算符不同; 无论给出什么类型,它总是意味着相同的事情,因为as运算符永远不会调用用户定义的转换。 因此,它可以用于强制转换非法的上下文中。

“as 运算符就像一个强制转换操作。但是,如果无法进行转换,as 将返回 null 而不是引发异常。”

您不会收到as运算符的编译时错误,因为编译器在使用as运算符时不会检查未定义的显式强制转换; 它的目的是允许尝试可能有效或无效的运行时强制转换,如果无效,则返回 null 而不是抛出异常。

无论如何,如果您打算处理fruit不是Apple ,您应该将您的支票实施为

var asApple = fruit as Appple;
if(asApple == null)
{
    //oh no
}
else
{
   //yippie!
}

回答为什么编译器不允许您按照自己的意愿编写代码的问题。 if 在运行时被评估,所以编译器不知道只有当它有效时才会进行转换。

为了让它工作,你“可以”在你的 if 中做这样的事情:

Apple apple = (Apple)(object)fruit;

这是关于同一问题的更多内容。

当然,使用as运算符是最好的解决方案。

它在 msdn 文档中有解释

as 运算符类似于强制转换操作。 但是,如果无法进行转换,as 将返回 null 而不是引发异常。 考虑以下示例:

expression as type 除了表达式变量只计算一次之外,代码等价于以下表达式。

表达式是类型? (type)expression : (type)null 请注意, as 运算符仅执行引用转换、可为空转换和装箱转换。 as 运算符不能执行其他转换,例如用户定义的转换,而应该使用强制转换表达式来执行这些转换。

基类类型的变量可以保存派生类型。 要访问派生类型的方法,必须将值强制转换回派生类型。 使用 as 将阻止您获得 InvalidCastException。 如果你想处理特定的空引用场景,你可以这样做。

public class Fruit
{
    public static explicit operator bool(Fruit D)
    {
         // handle null references
         return D.ToBoolean();
    }

    protected virtual bool ToBoolean()
    {
         return false;
    }
}

AS运算符关键字从 Visual Basic 继承其操作。

知情人士会告诉您,Visual Basic 是一种比 C# 功能更强大的语言,C# 本身就是在不断尝试制作类似 C 的语法语言,但具有 Visual Basic 的功能

这是因为 C 语法语言更受专业人士欢迎,而那些不标榜自己是 Basic 的语言也是如此。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM