【问题标题】:C# reference to loop variableC# 引用循环变量
【发布时间】:2009-07-03 07:05:06
【问题描述】:

在 C# 中是否有可能类似于以下内容

foreach (ref string var in arr) {
    var = "new value";
}

因此 var 变量被视为引用并分配给 var 会更改数组元素?

【问题讨论】:

    标签: c# reference


    【解决方案1】:

    更新循环没有这样的结构;迭代器是只读的。例如,以下提供了一个完全有效的迭代器:

    public IEnumerable<int> Get1Thru5() {
        yield return 1; yield return 2; yield return 3;
        yield return 4; yield return 5;
    }
    

    它将如何更新? 会更新什么

    如果数据是数组/列表/等,那么类似:

    for(int i = 0 ; i < arr.Length ; i++) {
        arr[i] = "new value";
    }
    

    或其他选项取决于特定容器。


    更新; 推送,扩展方法:

    public static void UpdateAll<T>(this IList<T> list, Func<T, T> operation) {
        for (int i = 0; i < list.Count; i++) {
            list[i] = operation(list[i]);
        }
    }
    static void Main() {
        string[] arr = { "abc", "def", "ghi" };
        arr.UpdateAll(s => "new value");
        foreach (string s in arr) Console.WriteLine(s);
    }
    

    【讨论】:

    • 您甚至可能希望将return list; 放在您的 UpdateAll 扩展方法的末尾,以便在直接用于诸如此类的分配时可以链接和/或返回它作为List&lt;string&gt; words = someWords.UpdateAll( s =&gt; s.Trim() );
    【解决方案2】:

    没有。 foreach 语句只是IEnumerable 接口之上的语法糖。这个接口定义了一个获取 enIEnumerator 的方法,该方法又具有进行只读枚举的方法:

    • 当前:对象
    • MoveNext() : bool
    • 重置():无效

    【讨论】:

    • 严格来说说它甚至不需要 IEnumerable/IEnumerator,而且它从不使用 Reset() - 但确实是 (+1)
    • 如果你使用的是数组,它根本不使用 IEnumerable。
    【解决方案3】:
    foreach(string s in strings)
    {
      Console.WriteLine(s);
    }
    

    是编译器的快捷方式:

    IEnumerator e = strings.GetEnumerator();
    string s;
    while(e.MoveNext())
    {
      s = e.Current;
      Console.WriteLine(s);
    }
    

    由于 IEnumerator.Current 是一个 get-only 属性,您无法设置该值。

    // Non-generic IEnumerator shown.
    interface IEnumerator
    {
      bool MoveNext();
      object Current { get; }
      void Reset();
    }
    

    如果您想支持一个可更新的枚举器,您需要自己创建它——但您将无法使用“foreach”,并且您必须围绕所有常见的 IEnumerable 类实现包装器。

    您必须分析您当前的情况并弄清楚如何更新。如果你使用的是 IList 接口,你可以这样做:

    for(int i = 0; i < strings.Count; ++i)
    {
      string s = strings[i];
      //do work
      s = s.ToUpperInvariant();
      strings[i] = s;
    }
    

    【讨论】:

    • 其实“string s”在outside循环;这对于理解臭名昭著的 foreach/capture 问题很重要。如果实现了 IDisposable,它将使用Dispose()
    • 更多的是为了演示而不是准确性,但你是对的,@Marc Gravell。我已将字符串移出循环。将 Dispose() 留给读者作为练习,因为它是本次讨论的正畸醇。
    【解决方案4】:

    在字符串的情况下,否; C# 字符串是不可变的(不能 改变)。如果你在枚举 不同的可变类型的对象, 你可以改变那些的属性 对象。

    只是为了说明 Jacob 在说什么。考虑下面的代码 sn-p:

    class MyInt
    {
        public int Val { get; set; }
        public MyInt(int val) { this.Val = val; }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            MyInt[] array = new MyInt[] { new MyInt(1), new MyInt(2) };
    
            foreach (var obj in array) Console.Write("{0}\t", obj.Val);
    
            foreach (var obj in array)
            { 
                obj = new MyInt(100); // This doesn't compile! the reference is read only
                obj.Val *= 10; // This works just fine!
            }
    
            foreach (var obj in array) Console.Write("{0}\t", obj.Val);
        }
    }
    

    确实,如果您尝试按上述方式分配给“obj”,您将收到编译时错误。但是没有什么能阻止您通过“obj”引用修改 MyInt 的属性

    【讨论】:

      【解决方案5】:

      在字符串的情况下,否; C# 字符串是不可变的(无法更改)。如果您在枚举不同的可变类型的对象,则可以更改这些对象的属性。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-12-23
        • 1970-01-01
        • 1970-01-01
        • 2011-02-19
        • 1970-01-01
        • 2010-12-28
        • 2014-11-11
        • 1970-01-01
        相关资源
        最近更新 更多