Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/275.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 迭代变量如何是只读的?_C#_Foreach_Iterator_Ienumerable_Cil - Fatal编程技术网

C# 迭代变量如何是只读的?

C# 迭代变量如何是只读的?,c#,foreach,iterator,ienumerable,cil,C#,Foreach,Iterator,Ienumerable,Cil,在C#规范的8.8.4中,它提供了以下示例: 形式的foreach语句 然后扩展到: 它还说: 迭代变量对应于具有 扩展到嵌入语句的范围 变量v在嵌入语句中是只读的 迭代变量如何设置为只读? 在C#中,这里不能使用readonly,const也不起作用 这是我举的一个例子 我查看了CIL代码,但看不到它使迭代变量为只读的任何地方: C#: class Program { static void Main(string[] args) { var enumerabl

在C#规范的8.8.4中,它提供了以下示例:

形式的foreach语句

然后扩展到:

它还说:

迭代变量对应于具有 扩展到嵌入语句的范围

变量v在嵌入语句中是只读的

迭代变量如何设置为只读?

在C#中,这里不能使用readonly,const也不起作用

这是我举的一个例子

我查看了CIL代码,但看不到它使迭代变量为只读的任何地方:

C#:

class Program
{
    static void Main(string[] args)
    {
        var enumerable = new List<string> { "a", "b" };

        foreach (string item in enumerable)
        {
            string x = item;
        }
    }
}
.method private hidebysig static 
    void Main (
        string[] args
    ) cil managed 
{
    // Method begins at RVA 0x2050
    // Code size 80 (0x50)
    .maxstack 3
    .entrypoint
    .locals init (
        [0] class [mscorlib]System.Collections.Generic.List`1<string> enumerable,
        [1] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>,
        [2] string item,
        [3] string x
    )

    IL_0000: nop
    IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
    IL_0006: dup
    IL_0007: ldstr "a"
    IL_000c: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<string>::Add(!0)
    IL_0011: nop
    IL_0012: dup
    IL_0013: ldstr "b"
    IL_0018: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<string>::Add(!0)
    IL_001d: nop
    IL_001e: stloc.0
    IL_001f: nop
    IL_0020: ldloc.0
    IL_0021: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<string>::GetEnumerator()
    IL_0026: stloc.1
    .try
    {
        IL_0027: br.s IL_0035
        // loop start (head: IL_0035)
            IL_0029: ldloca.s 1
            IL_002b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>::get_Current()
            IL_0030: stloc.2
            IL_0031: nop
            IL_0032: ldloc.2
            IL_0033: stloc.3
            IL_0034: nop

            IL_0035: ldloca.s 1
            IL_0037: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>::MoveNext()
            IL_003c: brtrue.s IL_0029
        // end loop

        IL_003e: leave.s IL_004f
    } // end .try
    finally
    {
        IL_0040: ldloca.s 1
        IL_0042: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>
        IL_0048: callvirt instance void [mscorlib]System.IDisposable::Dispose()
        IL_004d: nop
        IL_004e: endfinally
    } // end handler

    IL_004f: ret
} // end of method Program::Main
类程序
{
静态void Main(字符串[]参数)
{
var enumerable=新列表{“a”,“b”};
foreach(可枚举中的字符串项)
{
字符串x=项目;
}
}
}
CIL:

class Program
{
    static void Main(string[] args)
    {
        var enumerable = new List<string> { "a", "b" };

        foreach (string item in enumerable)
        {
            string x = item;
        }
    }
}
.method private hidebysig static 
    void Main (
        string[] args
    ) cil managed 
{
    // Method begins at RVA 0x2050
    // Code size 80 (0x50)
    .maxstack 3
    .entrypoint
    .locals init (
        [0] class [mscorlib]System.Collections.Generic.List`1<string> enumerable,
        [1] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>,
        [2] string item,
        [3] string x
    )

    IL_0000: nop
    IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
    IL_0006: dup
    IL_0007: ldstr "a"
    IL_000c: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<string>::Add(!0)
    IL_0011: nop
    IL_0012: dup
    IL_0013: ldstr "b"
    IL_0018: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<string>::Add(!0)
    IL_001d: nop
    IL_001e: stloc.0
    IL_001f: nop
    IL_0020: ldloc.0
    IL_0021: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<string>::GetEnumerator()
    IL_0026: stloc.1
    .try
    {
        IL_0027: br.s IL_0035
        // loop start (head: IL_0035)
            IL_0029: ldloca.s 1
            IL_002b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>::get_Current()
            IL_0030: stloc.2
            IL_0031: nop
            IL_0032: ldloc.2
            IL_0033: stloc.3
            IL_0034: nop

            IL_0035: ldloca.s 1
            IL_0037: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>::MoveNext()
            IL_003c: brtrue.s IL_0029
        // end loop

        IL_003e: leave.s IL_004f
    } // end .try
    finally
    {
        IL_0040: ldloca.s 1
        IL_0042: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>
        IL_0048: callvirt instance void [mscorlib]System.IDisposable::Dispose()
        IL_004d: nop
        IL_004e: endfinally
    } // end handler

    IL_004f: ret
} // end of method Program::Main
.method私有隐藏静态
真空总管(
字符串[]args
)cil管理
{
//方法从RVA 0x2050开始
//代码大小80(0x50)
.maxstack 3
.入口点
.init(
[0]类[mscorlib]系统.集合.泛型.列表'1可枚举,
[1] valuetype[mscorlib]System.Collections.Generic.List`1/枚举器,
[2] 字符串项,
[3] 字符串x
)
IL_0000:没有
IL_0001:newobj实例无效类[mscorlib]System.Collections.Generic.List`1::.ctor()
IL_0006:dup
IL_0007:ldstr“a”
IL_000c:callvirt实例无效类[mscorlib]System.Collections.Generic.List`1::Add(!0)
IL_0011:没有
IL_0012:dup
IL_0013:ldstr“b”
IL_0018:callvirt实例无效类[mscorlib]System.Collections.Generic.List`1::Add(!0)
IL_001d:没有
IL_001e:stloc.0
IL_001f:没有
IL_0020:ldloc.0
IL_0021:callvirt实例值类型[mscorlib]System.Collections.Generic.List`1/枚举器类[mscorlib]System.Collections.Generic.List`1::GetEnumerator()
IL_0026:stloc.1
尝试
{
IL_0027:br.s IL_0035
//回路启动(头部:IL_0035)
IL_0029:ldloca.s 1
IL_002b:调用实例!0 valuetype[mscorlib]System.Collections.Generic.List`1/Enumerator::get_Current()
IL_0030:stloc.2
IL_0031:没有
IL_0032:ldloc.2
IL_0033:stloc.3
IL_0034:没有
IL_0035:ldloca.s 1
IL_0037:调用实例bool valuetype[mscorlib]System.Collections.Generic.List`1/Enumerator::MoveNext()
IL_003c:brtrue.s IL_0029
//端环
IL_003e:离开s IL_004f
}//结束,再试一次
最后
{
IL_0040:ldloca.s 1
IL_0042:constrained.valuetype[mscorlib]System.Collections.Generic.List`1/枚举器
IL_0048:callvirt实例无效[mscorlib]System.IDisposable::Dispose()
IL_004d:没有
IL_004e:最后结束
}//结束处理程序
IL_004f:ret
}//方法程序结束::Main

迭代变量是只读的,因为写入时出错。试试看,你会明白的

它没有创建一个
readonly
字段,文档也没有说它创建了一个
readonly
字段。它不可能是
只读
字段,因为它不是字段

现在,这里有一个微妙的问题。假设
v
是可变值类型,并且您对该类型调用一个方法,该方法将
this
的字段进行变异,并传递
v
。对发生的事情做出预测。现在试试看;你说得对吗?你能解释一下发生了什么事吗?你现在怎么看待
v
是“只读”的说法?你会说这是一个bug,还是正确的行为


现在用
只读
字段试试同样的方法,看看结果如何。您认为这是正确的行为吗?

编译器中有一个特殊的案例代码,它对
foreach
块中的迭代变量强制执行只读约束。它不对应于语言中公开的任何修饰符,因此不能在这个特定语法之外显式地将局部变量声明为只读

从概念上讲,此约束在展开之前应用。也就是说,如果对迭代变量有任何赋值,编译器将生成一个错误。否则,代码将展开。在扩展代码中,对
v
没有特别的约束,因为它只是一个正则局部变量。因此,IL中也不存在约束


那么为什么在
foreach
-语法中存在这种特殊的只读约束呢?只有语言设计师才能回答这个问题,但我想这只是为了避免混淆。如果迭代器变量是可赋值的,您可能会认为可以通过这种方式修改实际集合,但实际上不会发生任何事情,因为基础枚举数是只读的。

有一秒钟,我以为我在完成教程的一章后阅读了挑战部分。@Backwards\u Dave:听起来您的问题是“C#规范通过创建一些等效代码来描述foreach循环的含义,但在描述代码的文本中进一步说明变量是只读的;有没有办法在变量是只读的情况下创建一些等价的代码?“没有。如果有,那么我们就不必添加额外的文本来解释它是只读的。我最后几段的重点是让您思考您的猜测”是否只是编译器中的某个规则阻止您修改变量?”“是的。而且,该规则不会改变变量作为变量的分类,这意味着它在通过引用传递时不会按值复制,这与实
只读
字段不同。@Backwards\u Dave:您发现foreach循环中的只读变量是只读的,因为它可能无法直接写入;变量