列表.ForEach - 传递给 ForEach 的对象是引用还是副本

本文关键字:ForEach 对象 引用 副本 列表 | 更新日期: 2023-09-27 18:30:33

使用 List<T>.ForEach 时,传入的对象是传入的对象,即下面示例中通过引用或值传递的j

MyList.ForEach( j => {} );

如果我要做这样的事情:

MyList.ForEach( j => {
    j.someOtherList.Add("value");
});

这是作用于j的副本还是作用于原始对象?

列表<T>.ForEach - 传递给 ForEach 的对象是引用还是副本

根本没有传入的对象 - 有一个类型 T 的值。如果T是引用类型,则该值是引用。该值按值传递,就像正常情况一样。

现在假设T是某个引用类型,其成员名为 someOtherList ,那么您的代码确实会影响 j 的值所引用的对象 - 但 j 的值只是一个引用。列表中的值将是相同的引用,因此,如果您随后(例如)再次迭代列表,您将能够看到效果。

重要的是,如果您将代码更改为:

MyList.ForEach( j => {
    j = new WhateverYourTypeIs()
});

这根本不会影响列表。

我怀疑您可能对按值传递和按引用传递的工作方式有些困惑,尤其是在 C# 上下文中。我有一篇关于这个主题的文章可能会对你有所帮助。

这是一种注释(太长,无法放入注释字段)。

Jon Skeet已经给出了答案:它是"正常"传递的,即按值传递,其中值是值类型的整个对象本身,而是对引用类型的对象"位置"的引用。因此,问题中的示例可能会"工作"(假设someOtherListj类型的引用类型成员)。

但只是为了好玩,我写了一个替代ForEachByRef,它是 by-ref:

public delegate void ActionByRef<J>(ref J j);
public static class MyExtensions
{
    public static void ForEachByRef<T>(this List<T> list, ActionByRef<T> action)
    {
        for (int i = 0; i < list.Count; ++i)
        {
            T t = list[i];
            action(ref t);
            list[i] = t;
        }
    }
}

然后这有效:

static void Main()
{
    var testList = new List<string> { "alpha", "bravo", "charlie", };
    testList.ForEachByRef((ref string j) => { j = "changed"; });
}

(我不建议在"真实"代码中实际使用像ForEachByRef这样的方法;这只是为了说明。