C# 为什么一个直接演员失败了,但是;作为「;测试受约束泛型类型时运算符是否成功?

C# 为什么一个直接演员失败了,但是;作为「;测试受约束泛型类型时运算符是否成功?,c#,generics,.net-4.0,casting,constraints,C#,Generics,.net 4.0,Casting,Constraints,``在编译一些使用带有类型约束的泛型的C#代码时,我遇到了一个有趣的好奇心。我已经编写了一个快速测试用例来进行说明。我正在Visual Studio 2010中使用.NET 4.0 namespace TestCast { public class Fruit { } public class Apple : Fruit { } public static class Test { public static void TestFruit<

``在编译一些使用带有类型约束的泛型的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;
            }
        }
    }
}
有人能解释一下为什么会这样吗?

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

在任何情况下,如果您计划处理
水果
不是
苹果
的情况,您应该按照以下步骤执行检查:

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

在msdn文档中解释

as运算符类似于强制转换操作。但是,如果转换不可能,as将返回null而不是引发异常。考虑下面的例子:

表达式作为类型 该代码与以下表达式等效,只是表达式变量只计算一次

表达式是类型?(类型)表达式:(类型)null
请注意,as运算符仅执行引用转换、可为null的转换和装箱转换。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;
    }
}

来回答为什么编译器不允许您按照自己的意愿编写代码的问题。if在运行时得到计算,因此编译器不知道强制转换只有在它有效时才会发生

要使其发挥作用,您可以在if中执行以下操作:

Apple apple = (Apple)(object)fruit;
关于同一个问题,还有更多的问题


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

我用这个问题作为分析的基础。谢谢你的提问

有人能解释一下为什么会这样吗

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

让我把问题说得更清楚一些:

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

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

当被称为
M
时,您认为它应该做什么

void M(T T),其中T:水果
{
香蕉b=(香蕉)t;
}
大多数阅读代码的人都会说,这应该称为用户定义的从苹果到香蕉的转换。但是C类泛型不是C++模板;对于每个通用构造,该方法不是从头开始重新编译的。相反,该方法只编译一次,在编译过程中,每个操作符(包括强制转换)的含义都是针对每个可能的泛型实例化确定的

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

相反,你要做的是:

void M<T>(T t) where T : Fruit
{
    Banana b = (Banana)(object)t;
}
void M(T T),其中T:水果
{
香蕉b=(香蕉)(对象)t;
}
现在两种转换都很清楚了。对象的转换是隐式引用转换;对香蕉的转换是显式引用转换。用户定义的转换永远不会被调用,如果这是用Cherry构造的,那么错误发生在运行时,而不是编译时,就像从对象强制转换时一样


as
操作符与cast操作符不同;无论给定什么类型,它总是意味着相同的东西,因为
as
运算符从不调用用户定义的转换。因此,它可以在强制转换非法的上下文中使用。

ASOperator关键字继承了Visual Basic的操作

有知识的人会告诉你,VisualBasic是一种比C#更强大的语言,C#本身就是一种不断尝试的类似C的语法语言,但具有VisualBasic的功能


这是因为C语法语言更受专业人士的欢迎,那些不宣传自己是基础语言的语言也是如此。

那么“是”有什么用呢?我知道“as”允许在运行时进行可能无效的强制转换,在这种情况下返回null,但是如果检查null是处理这个问题的合理方法,“is”给了您什么?以我现有的方式使用“是”有错吗?@hatch22使用
Is
是为了在不需要将值强制转换为派生类型的情况下确定类型。@Servy:好的,这很有意义。我仍然不明白为什么编译器不允许在运行时尝试强制转换并抛出无效的强制转换异常,这似乎是一个合适的响应。为什么要将其标记为错误呢?在这种情况下,发生这种情况是因为您是从泛型强制转换的,即使泛型被限制为
水果
。显然,编译器不考虑在铸造时对泛型的约束,这对我来说是新的。如果从方法中移除泛型,编译器不会抱怨,但是运行时会。@ Preston GulnLo:编译器在浇铸时确实考虑了约束,而不是按您认为的方式进行约束。看到我的名字了吗
void M<T>(T t) where T : Fruit
{
    Banana b = (Banana)t;
}
void M<T>(T t) where T : Fruit
{
    Banana b = (Banana)(object)t;
}