简体   繁体   English

C#类型转换不一致?

[英]C# type conversion inconsistent?

In C#, I cannot implicitly convert a long to an int . 在C#中,我无法将long隐式转换为int

long l = 5;
int i = l;  // CS0266: Cannot implicitly convert type 'long' to 'int'. An explicit conversion exists (are you missing a cast?)

This produces said error. 这产生了所述错误。 And rightly so; 是这样的; if I do that, I am at risk of breaking my data, due to erroneous truncation. 如果这样做,由于错误的截断,将有可能破坏我的数据。 If I decide that I know what I am doing, then I can always do an explicit cast, and tell the compiler that it's okay to truncate, I know best. 如果我决定知道自己在做什么,那么我总是可以进行显式强制转换,并告诉编译器可以截断,我知道最好。

int i = (int)l;  // OK

However, the same mechanism does not seem to apply when using a foreach loop. 但是,在使用foreach循环时,似乎没有应用相同的机制。

IList<long> myList = new List<long>();
foreach (int i in myList)
{
}

The compiler does not even generate a warning here, even though it is essentially the same thing: an unchecked truncation of a long to an int , which might very well break my data. 即使本质上是同一件事,编译器甚至不会在此处生成警告: longint的未经检查的截断,这很可能会破坏我的数据。

So my question is simply: Why does this foreach not create the same error as the variable assignment does? 所以我的问题很简单:为什么这个foreach不会产生与变量赋值相同的错误?

UPDATE: This question was the subject of my blog in July of 2013 . 更新:这个问题是我2013年7月博客的主题 Thanks for the great question! 感谢您提出的好问题!

Why does this foreach not create the same error as the variable assignment does? 为什么此foreach不会产生与变量赋值相同的错误?

"Why" questions are difficult to answer because I don't know the "real" question you're asking. “为什么”问题很难回答,因为我不知道您要问的“真正”问题。 So instead of answering that question I'll answer some different questions. 因此,我将回答一些不同的问题,而不是回答该问题。

What section of the specification justifies this behaviour? 规范的哪一部分证明了这种行为?

As Michael Liu's answer correctly points out, it is section 8.8.4. 正如迈克尔·刘的答案正确指出的那样,这是第8.8.4节。

The whole point of an explicit conversion is that the conversion must be explicit in the code; 显式转换的全部要点是,转换必须在代码中是显式的。 that's why we have the cast operator; 这就是为什么我们要使用强制转换运算符; it's waving a big flag that says "there's an explicit conversion right here". 它挥舞着一个大旗,上面写着“这里有一个明确的转换”。 This is one of the few times in C# where an explicit conversion is not extant in the code. 这是C#中几次在代码中不存在显式转换的情况之一。 What factors motivated the design team to invisibly insert an "explicit" conversion? 哪些因素促使设计团队隐式地插入“显式”转换?

The foreach loop was designed before generics. foreach循环是在泛型之前设计的。

ArrayList myList = new ArrayList();
myList.Add("abc");
myList.Add("def");
myList.Add("ghi");

You don't want to have to say: 您不想说:

foreach(object item in myList)
{
    string current = (string)item;

In a world without generics you have to know ahead of time what types are in a list, and you almost always do have that knowledge . 在没有泛型的世界中,您必须提前知道列表中有哪些类型,并且您几乎总是拥有该知识 But this information is not captured in the type system. 但是,此信息未在类型系统中捕获。 Therefore, you have to tell the compiler somehow, and you do that by saying 因此,您必须以某种方式告诉编译器,然后通过说

foreach(string item in myList)

This is your assertion to the compiler that the list is full of strings, just like a cast is an assertion that a particular item is a string. 这是向编译器断言该列表中充满了字符串,就像强制转换断言特定项是字符串一样。

You are completely correct that this is a misfeature in a world with generics. 您完全正确的认为,在具有泛型的世界中,这是错误的功能。 Since it would be breaking to change it now, we're stuck with it. 由于现在更改它会很困难,因此我们坚持使用它。

The feature is quite confusing; 该功能非常混乱; when I first started programming C# I assumed that it had the semantics of something like: 刚开始编程C#时,我以为它具有类似以下内容的语义:

while(enumerator.MoveNext())
{
    if (!(enumerator.Current is string) continue;
    string item = (string)enumerator.Current;

That is, "for each object of type string in this list, do the following", when it really is "for each object in this list assert that the item is a string and do the following..." (If the former is what you actually want then use the OfType<T>() extension method.) 也就是说,如果确实是“对于此列表中的每个对象断言该项目是一个字符串,然后执行以下操作……”(如果前一个是然后使用OfType<T>()扩展方法。)

The moral of the story is: languages end up with weird "legacy" features when you massively change the type system in version 2. 这个故事的寓意是:当您在版本2中大规模更改类型系统时,语言最终会具有怪异的“旧版”功能。

Should the compiler produce a warning for this case in modern code, where generics are being used? 在使用泛型的现代代码中,编译器是否应针对这种情况发出警告?

I considered it. 我考虑过了 Our research showed that 我们的研究表明

foreach(Giraffe in listOfMammals)

is so common that most of the time we'd be giving a warning for correct code. 非常普遍 ,以至于大多数时候我们都会警告您正确的代码。 That creates trouble for everyone who compiles with "warnings as errors" turned on, and it's generally speaking badness to have a warning on code that is yes maybe a bit smelly but actually correct . 这给每个打开“警告即错误”的编译人员带来麻烦,通常来说,对代码发出警告是不好的,虽然这可能有点臭,但实际上是正确的 We decided to not pursue the warning. 我们决定不执行警告。

Are there other situations where the C# compiler invisibly inserts explicit conversions? 还有其他情况下C#编译器会无形地插入显式转换吗?

Yes. 是。 In fact someone asked a question about that just a few hours after this one: 实际上,在此之后仅几个小时,有人问了一个问题:

Compiler replaces explicit cast to my own type with explicit cast to .NET type? 编译器将显式强制转换为.NET类型吗?

There are some extremely obscure interop scenarios where explicit conversions are inserted as well. 在某些非常隐蔽的互操作方案中,还会插入显式转换。

As defined in §8.8.4 of the C# 4.0 specification, a foreach statement of the form 如C#4.0规范的§8.8.4所定义,以下形式的foreach语句

foreach (V v in x) embedded-statement

is expanded to 扩展到

{
    E e = ((C)(x)).GetEnumerator();
    try {
        V v;
        while (e.MoveNext()) {
            v = (V)(T)e.Current; // <-- note the explicit cast to V
            embedded-statement
        }
    }
    finally {
        … // Dispose e
    }
}

where C is the "collection type" and T is the "element type" inferred from x . 其中C是“集合类型”, T是从x推断出的“元素类型”。

The cast to V ( int in your case) is what allows your example to compile. 强制转换为V (在您的情况下为int )可以编译您的示例。

The likely reason for the cast to V : in C# 1.0, before generics was added to the language, an explicit cast was usually needed anyway when enumerating through a collection like ArrayList , because the compiler could not automatically figure out the type of values in the collection. 强制转换为V的可能原因:在C#1.0中,在将泛型添加到语言中之前,通过ArrayList类的集合枚举时,无论如何通常都需要显式ArrayList ,因为编译器无法自动找出变量中的值类型。采集。

The simple answer is foreach does an explicit cast behind the scenes. 简单的答案是foreach在幕后进行了明确的转换。 Another example: 另一个例子:

    public class Parent { }
    public class Child : Parent { }

    IList<Parent> parents = new List<Parent>()
    {
        new Parent()
    };
    foreach (Child child in parents) { }

This will also not generate a compiler error, but will throw an InvalidCastException at runtime. 这也不会产生编译器错误,但会在运行时InvalidCastException

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

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