改变C#和C ++ / CLI之间的foreach迭代变量和执行的差异(Changing forea

2019-10-19 02:28发布

考虑下面的C#代码。

string[] stringArray = new string[10];
foreach (string s in stringArray)
    s = "a new string";  // Compiler error - Can't assign to foreach iteration variable

现在考虑下面的有效的 C ++ / CLI代码。

array<String^>^ stringArray = gcnew array<String^>(10);
for each(String^% s in stringArray)
    s = "a new string"; 

foreach与阵列型使用时,编译器将其转换为正常for循环。 这个实现是同为C#和C ++ / CLI。 所以我想如果C ++ / CLI可以实现这一点,为什么不为C#编译器?

此错误是有道理的,当类型不是一个数组foreach会被编译成GetEnumerator通话,并使用枚举迭代。 但我认为它可以允许数组类型。

有什么想法吗?

作为一个侧面说明,下面是一个合法的C ++ / CLI代码太多,但不会产生预期的结果。

List<String^>^ stringList = gcnew List<String^>(10);
for each(String^% s in stringList)
    s = "a new string"; // I think this should be prevented by compiler as it makes no sense.

Answer 1:

似乎有这里三个不同的问题:

  1. 为什么C ++允许你分配给for each迭代变量?
  2. 为什么不C#?
  3. 在C ++和C#编译器为什么不同的表现?

这些问题的答案相当简单:

  1. 因为C ++团队决定不明确禁止它,在技术上迭代变量仅仅是一个局部变量-它没有得到特殊待遇。

  2. 因为C#队决定禁止它,因为(最有可能),他们认为这会导致错误或不正确的代码。 分配到任何循环变量通常被视为代码味道。

  3. 因为C ++团队和C#团队是不同的球队。 C ++一直是一种语言,可以让你拍摄自己的脚,如果你选择,并且走得更远,他交给你了装满子弹的枪。 C#会经常尝试执行“正确的代码”的规则。

有可能实际上是在这里另外一个问题,那就是:

  • 为什么C#编译foreachfor ,如果不允许转让? 或者,反过来 - 为什么不允许这样做,如果这就是它被如何呢编译?

实际上有两个答案,这一个:

  • 因为它的速度更快。 foreach上操作IEnumerable ,这需要一个新IEnumerator被实例化类。 数组类型为特殊类型由编译器识别,因此,如果编译器已经知道了IEnumerable实际上是一个Array ,然后将其编译为索引访问,而不是,这是便宜得多。

    这一点表现的调整是一个简单的实现细节 ,但是, 它不是规范的一部分,如果你能够编写依赖于具体的实现代码,C#团队将无法再更改,落实不破坏现有的代码。 他们肯定会想避免这种情况。

  • 因为它实际上并没有重要的途径之一可能会认为它。 如果你能做到在C#中的任务,你会不会实际上是修改阵列,只有最初举行阵列东西局部变量的内容。 这同样的“让人很难写不正确的代码”类别下的瀑布-如果没有结构让你赋给变量,有些程序员可能会认为,这实际上改变集合 ,这将是错误的。

我认为应该解释得很好。



Answer 2:

这是因为C#是使用雾里看花(即魔术)。 你回来在foreach的变量不是数组中的实际项目,它是由迭代对象...还是其他什么东西制作的拷贝。 我们真的不知道(当然我们这样做,但我们必须打破了抽象层,并期待在迭代器对象的实现。)

如果你想改变这些值在数组中,你必须直接与阵列的接口来处理访问这些项目。 在C ++中出现这种情况,但主要是由错误(很多C ++的是这样的,原来的实现实际上是宏和预处理)。 在C#它明确定义不工作 - 这样一个编译器的消息。 (详见第5.3.3.16 规范。 )



Answer 3:

我想你已经回答了你自己的问题,当你的状态,对于C ++的情况:

foreach与阵列型使用时,编译器将其转换为正常for循环。

如果你的代码的C#作为一个for循环,你可以做到这一点。

此上的评论MSDN页面解释更多,但它归结为一个事实,即通过修改字符串要更改集合的索引-这就是为什么环断裂。



Answer 4:

重新霍根:是的,一个拷贝被它作出的。 事实上,当我跑进去(今天)我是指望它是一个modifieable副本:

foreach (string Token in tokenize(Command))
{
foreach (KeyValuePair<string, string> Replacement in TokensToReplace)
    {
    if (Token==Replacement.Key)
        {
        Token = Replacement.Value;
        }
    }
TokenList.Add(Token);
}

我认为这是不幸的是,这是行不通的。



Answer 5:

Aaronaught,循环没有扭转它只是创建一个字符串变量“令牌”,并与记号化值初始化该功能。 之后,它没有任何与记号化的任何内部。 我试图让在是循环变量不应该是一些神奇的指针变成一个集但得到由“字符串令牌=”就好像它是在创建for或while循环创建的对象。

我不是想改变一些内部的收藏价值,但覆盖的局部变量。 它不应该有对收集数据的后果,因为指向旧字符串被分配后丢失。

在C ++中它的工作原理。



文章来源: Changing foreach iteration variable and implementation differences between C# and C++/CLI