在foreach中使用的替代ref的方法?

16

我有一个签名类似于的修改方法

private bool Modify(ref MyClass obj);

会对obj进行修改,并通过返回值说明成功。 Modify并没有重新分配引用(我知道这样不行),只是修改实例字段,所以我想使用它来执行以下操作:

foreach(MyClass obj in myList)
{
    bool success = Modify(obj);
    // do things depending on success
}

我在编译时遇到了问题,因为obj 没有通过ref关键字传递。但是,如果我这样加入 ref 关键字:

bool success = Modify(ref obj);

我收到一个错误信息:"无法将obj用作ref/out,因为它是'foreach'迭代变量"。我知道 foreach 使用不可变迭代器,这就是为什么它不起作用的原因。

我的问题是,有什么最简单的替代方法可以让类似这样的操作起作用吗?

我已经尝试使用

foreach(int i = 0; i < myList.Count; i++)
{
    bool success = Modify(ref myList[i]);
    // do things depending on success
}

但是他们会收到 "a property or indexer may not be passed as an out or ref parameter" 错误。

感谢您的帮助。


2
也许可以使用简单的for循环代替foreach - Mitya
10
为什么需要通过引用传递对象到这个 Modify 函数? - James Wierzba
7
你可以修改obj的属性而不使用ref。使用ref允许你修改引用本身,这几乎肯定不是你想要的。 - Jon B
4
如果您正在更新属性或字段,不需要使用 ref 参数。如果您要完全重新分配引用,则这是一个单独的问题。请说明您如何修改它。 - Anthony Pegram
1
请更新您的问题,包括您如何在Modify函数内部使用obj - Scott Chamberlain
显示剩余7条评论
5个回答

22

C#中的任何类型都是按值传递的。当你将类的实例传递给方法时,实际上传递的不是实例本身,而是对它的引用,该引用本身是按值传递的。因此,你实际上是通过引用来传递类的实例——这就是为什么称它们为引用类型的原因。

在你的情况下,你只需要修改方法中引用值所引用的现有实例,无需使用ref关键字。

foreach(var m in myList)
{
    MyMethod(m);
}

MyMethod(MyClass instance)
{
    instance.MyProperty = ...
}

如果您真的将引用按引用传递,您将在循环内的每次迭代中重新分配obj,这在foreach块中是不允许的。这类似于以下内容:

foreach(var m in myList)
{
    m = new MyClass();
}

另一方面,您也可以使用传统的for循环。但是,您需要一个临时变量来存储方法的结果:

for(int i = 0; i < myList.Length; i++)
{
    var tmp = myList[i];
    MyMethod(ref tmp);
    myList[i] = tmp;
}

3
这确实回答了所提出的概念问题,但是否是 OP 所需求的正确解决方案尚不确定。 - James Wierzba

10
您所述的是"修改不是重新分配引用",因此Modify(ref MyClass)函数没有必要通过ref来传递参数。您应该能够通过按值传递对象引用(请澄清一下那些"修改"具体指什么),即删除ref关键字来进行相同的"修改"操作。因此,在此处进行修复的方法应该是将函数签名从Modify(ref MyClass)更改为Modify(MyClass)

3
修改很复杂,我认为与例子无关。 - Jon Deaton

1
你需要将数组转换为 Span:
int[] nums = new[] { 1, 2, 3, 4, 5 };

int i = 0;
foreach (ref var item in nums.AsSpan())
{
    item += 10;
    Console.WriteLine(nums[i++]);
}

输出:
11
12
13
14
15
您可以修改迭代变量,因为Span Enumerator的Current属性是ref T类型。

1
使用临时变量来绕过消息。
foreach(MyClass obj in myList)
{
    MyClass objTemp = obj;
    bool success = Modify(ref objTemp);
    // do things depending on success
}

private MyMethod(ref MyClass instance)
{
    instance.MyProperty = ...
}

0

使用LINQ解决了这个问题。

我的代码:

    private static List<string> _test = new List<string>();

    public List<string> Test { get => _test; set => _test = value; }


    static void Main(string[] args)
    {
        string numString = "abcd";

        _test.Add("ABcd");
        _test.Add("bsgd");

        string result = _test.Where(a => a.ToUpper().Equals(numString.ToUpper()) == true).FirstOrDefault();

        Console.WriteLine(result + " linq");
    }

2
这个回答如何解决在循环中修改列表的问题? - MakePeaceGreatAgain
我建议一种解决方法,可以不使用for或foreach语句来解决它。 - SHO
提问者似乎在寻找比使用for或foreach中的ref更简单的方法。 - SHO

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接