为什么要铸造CS0030,而“as”作品? [英] Why does casting give CS0030, while "as" works?

查看:116
本文介绍了为什么要铸造CS0030,而“as”作品?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个通用的方法:

pre $ T $(T x){
return x;
}

到目前为止这么好。但是如果它是一个Hashtable,我想要做一些特殊的事情。 (我知道这是一个完全人为的例子, Foo()也不是一个非常令人兴奋的方法。)



pre $ if(typeof(T)== typeof(Hashtable)){
var h =((Hashtable)x); // CS0030:无法将类型'T'转换为'System.Collections.Hashtable'
}

织补。但是,公平地说,我实际上无法确定这是否合法C#或不合适。那么,如果我尝试以不同的方式做什么?

  if(typeof(T)== typeof(Hashtable)){ 
var h = x as Hashtable; //工作(并且不,不是空的)
}

奇怪的。根据MSDN,表达式作为类型是(除了对表达式求两次),与表达式相同的类型是? (type)expression:(type)null



如果我尝试使用文档中的等效表达式,会发生什么情况?

(b)

$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ :(Hashtable)null); // CS0030:无法将类型'T'转换为'System.Collections.Hashtable'
}

在我看到的转换和作为之间唯一记录的区别是,因为运算符仅执行引用转换和装箱转换。也许我需要告诉它我使用的是引用类型?

  T Foo(T x)其中T:class {
var h =((Hashtable)x); // CS0030:无法将类型'T'转换为'System.Collections.Hashtable'
return x;
}

发生什么事了?为什么作为工作正常,而铸造甚至不会编译?如果演员工作,或者 as 不起作用,或者在演员和之间有一些其他语言区别,那么是不是在我发现这些MSDN文档?

解决方案

Ben的答案基本上是钉在头上,但要扩大有一点:$ b​​
$ b

这里的问题是,人们有一个自然的期望,即一个泛型方法会做同样的事情,如果给定类型,等价的非泛型方法会做在编译时。在你的特定情况下,人们会认为如果T很短,那么(int)t 应该做正确的事情 - 把短变成一个int。并且(double)t 应该将空格变成双精度。如果T是字节,那么(int)t 应该将该字节转换为int,并且(double)t 应该把字节变成双倍......现在也许你开始看到这个问题。我们必须生成的通用代码基本上必须在运行时再次启动编译器并进行完整类型分析,然后动态生成代码以按预期方式执行转换。



这可能很昂贵;我们在C#4中添加了该功能,如果这是您真正想要的功能,您可以将这些对象标记为dynamic类型,并且编译器的一个简化版本将在运行时再次启动,并为您执行转换逻辑。



但那昂贵的东西通常不是人们想要的。



as逻辑是 不如铸造逻辑复杂,因为它不必处理除装箱,拆箱和参考转换以外的任何转换。它不需要处理用户定义的转换,它不必处理像将双字节转换为八字节数据结构的单字节数据的字节到双精度这样的花哨的表示改变转换,等等。 p>

这就是为什么as在泛型代码中是允许的,但是强制转换不是。

所有这些都说:你几乎当然做错了。如果您必须在泛型代码中进行类型测试,那么您的代码不是通用的。这是一个非常糟糕的代码味道。


Suppose I have a generic method:

T Foo(T x) {
    return x;
}

So far so good. But I want to do something special if it's a Hashtable. (I know this is a completely contrived example. Foo() isn't a very exciting method, either. Play along.)

if (typeof(T) == typeof(Hashtable)) {
    var h = ((Hashtable)x);  // CS0030: Cannot convert type 'T' to 'System.Collections.Hashtable'
}

Darn. To be fair, though, I can't actually tell if this should be legal C# or not. Well, what if I try doing it a different way?

if (typeof(T) == typeof(Hashtable)) {
    var h = x as Hashtable;  // works (and no, h isn't null)
}

That's a little weird. According to MSDN, expression as Type is (except for evaluating expression twice) the same as expression is type ? (type)expression : (type)null.

What happens if I try to use the equivalent expression from the docs?

if (typeof(T) == typeof(Hashtable)) {
    var h = (x is Hashtable ? (Hashtable)x : (Hashtable)null);  // CS0030: Cannot convert type 'T' to 'System.Collections.Hashtable'
}

The only documented difference between casting and as that I see is "the as operator only performs reference conversions and boxing conversions". Maybe I need to tell it I'm using a reference type?

T Foo(T x) where T : class {
    var h = ((Hashtable)x);  // CS0030: Cannot convert type 'T' to 'System.Collections.Hashtable'
    return x;
}

What's going on? Why does as work fine, while casting won't even compile? Should the cast work, or should the as not work, or is there some other language difference between casting and as that isn't in these MSDN docs I found?

解决方案

Ben's answer basically hits the nail on the head, but to expand on that a bit:

The problem here is that people have a natural expectation that a generic method will do the same thing that the equivalent non-generic method would do if given the types at compile time. In your particular case, people would expect that if T is short, then (int)t should do the right thing -- turn the short into an int. And (double)t should turn the short into a double. And if T is byte, then (int)t should turn the byte into an int, and (double)t should turn the byte into a double... and now perhaps you begin to see the problem. The generic code we'd have to generate would basically have to start the compiler again at runtime and do a full type analysis, and then dynamically generate the code to do the conversion as expected.

That is potentially expensive; we added that feature in C# 4 and if that's what you really want, you can mark the objects as being of type "dynamic" and a little stripped-down version of the compiler will start up again at runtime and do the conversion logic for you.

But that expensive thing is typically not what people want.

The "as" logic is far less complicated than the cast logic because it does not have to deal with any conversions other than boxing, unboxing and reference conversions. It does not have to deal with user-defined conversions, it does not have to deal with fancy representation-changing conversions like "byte to double" that turn one-byte data structures into eight-byte data structures, and so on.

That's why "as" is allowed in generic code but casts are not.

All that said: you are almost certainly doing it wrong. If you have to do a type test in generic code your code is not generic. This is a really bad code smell.

这篇关于为什么要铸造CS0030,而“as”作品?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆