C# Foreach循环将迭代变量传递给一个有ref和没有ref的方法-不明白为什么它不允许有ref
今天早上,我一直在努力理解以下内容,我希望有人能很好地解释为什么这样做行不通。我在C# Foreach循环将迭代变量传递给一个有ref和没有ref的方法-不明白为什么它不允许有ref,c#,foreach,C#,Foreach,今天早上,我一直在努力理解以下内容,我希望有人能很好地解释为什么这样做行不通。我在LinqPad上做了一个例子,复制了我的问题: void Main() { var myPeople = new People(); myPeople.MyPeople.Add(new Person { Name = "Joe", Surname = "Doe" }); foreach (var person in myPeople.MyPeople) { //this is inte
LinqPad
上做了一个例子,复制了我的问题:
void Main()
{
var myPeople = new People();
myPeople.MyPeople.Add(new Person { Name = "Joe", Surname = "Doe" });
foreach (var person in myPeople.MyPeople)
{
//this is intermediate variable is still a reference to the iteration variable
var intermediate = person;
myPeople.ChangeName(ref intermediate);
//both names were changed, so why cant i just pass in 'person' directly since its still changing it.
intermediate.Name.Dump();
person.Name.Dump();
}
}
public class People
{
public People()
{
MyPeople = new List<Person>();
}
public List<Person> MyPeople { get; set; }
public void ChangeName(ref Person person)
{
person.Name = "Changed";
}
}
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
}
void Main()
{
var myPeople=新人();
myPeople.myPeople.Add(newperson{Name=“Joe”,姓氏=“Doe”});
foreach(myPeople.myPeople中的var person)
{
//这是中间变量,仍然是迭代变量的引用
var中间值=人;
myPeople.ChangeName(ref intermediate);
//两个名字都变了,既然“person”还在变,为什么我不能直接输入它呢。
intermediate.Name.Dump();
person.Name.Dump();
}
}
公营阶层人士
{
公众人士()
{
MyPeople=新列表();
}
公共列表MyPeople{get;set;}
公共无效变更名称(参考人)
{
person.Name=“已更改”;
}
}
公共阶层人士
{
公共字符串名称{get;set;}
公共字符串姓氏{get;set;}
}
因此,解释了foreach
变量是只读变量,这就是为什么不能用另一个变量替换它-这很有意义,但是如果我用ref
将它传递到上面的ChangeName
方法中,编译器将不允许它。但是,如果我删除ChangeName
方法上的ref
,它将允许我传入foreach变量,但从技术上讲,它仍然是reference
,而不明确指出它是ref
对吗
那么为什么会发生这种情况呢?此外,如果我把它赋给上面的一个中间变量,那么它将允许我以ref
的形式传递它,即使它仍然是对原始变量的引用,所以技术上指向相同的内存位置,这意味着相同的对象,对吗
所以我的问题是为什么C#编译器不让我将迭代变量传递给一个带有ref
Rafalon对问题的进一步澄清:
当person
变量指向与intermediate
变量相同的对象时,为什么编译器不允许myPeople.ChangeName(ref person),但允许myPeople.ChangeName(ref intermediate)?最后解释了所有这一切;常规foreach
的l值被视为“只读”-您不能:
person=newperson();
比如说。ref
用法要求参数not是只读的,否则我们就违反了减少只读的承诺,因为该方法可能会修改原本是只读的内容。在这种情况下,由于Person
是一个类,因此它只与实际引用本身相关,而与底层对象无关
不过,有一个好消息:我们现在在中有了,这类似于ref
,但是对于只读场景,所以:将public void ChangeName(ref Person)
更改为public void ChangeName(Person)
,它应该可以工作:
myPeople.ChangeName(亲自);
对此的限制是,在ChangeName
方法中,您不能执行以下操作:
public void ChangeName(亲自)
{
person=新的person();//无效
}
强调:在
中的用法实际上并不适用于类;它用于struct
,尤其是readonly struct
——以避免复杂和大值类型的防御堆栈副本
在C#的最新版本中,还有一个ref readonly
概念,根据该概念,l值是refFoo
(适用于任何类型的Foo
)。这需要一个带有public-ref-Foo-Current{get;}
访问器的自定义迭代器,而不是public-Foo-Current{get;}
访问器,即它显式绑定到ref-return。当l值为ref时,可以在采用ref
的方法中使用它
重要的是,尽管我知道OP知道这一点:在这个场景中,您不需要ref
,因为Person
本身就是一个类;代码在没有ref的情况下运行正常,对象将正确更新。当您传递ref intermediate时,您将引用传递给变量intermediate,而不是它所指向的对象。如果将一个新的Person分配给ChangeName中的Person变量,中间变量将被更改
这就是为什么不允许在foreach中传递ref person,因为您可以更改foreach变量的值。“但从技术上讲,它仍然是一个引用”-这里的区别是“一个引用”与“一个引用的引用”;但更让人困惑的是,c#“latest”支持ref foreach
,其中l值是一个引用(即在这种情况下是ref Person
),在这种情况下它会工作!注意:大多数人永远不需要知道或关心ref-foreach
:)假设我们将public-void-ChangeName(ref-Person-Person){Person=null;}
。所需的forloop
行为是什么?在intermediate
的情况下,它是intermediate==null
,而循环变量(person
)没有更改。因此问题是“为什么编译器不允许myPeople.ChangeName(ref-person)
但允许myPeople.ChangeName(ref-intermediate)
当person=intermediate
时,”,对吗?//这是中间变量,它仍然是对迭代变量的引用
不,它不是。它是对迭代
引用的同一对象的引用。这种区别可能看起来很微妙,但很重要。请重新阅读MSDN中关于ref和out关键字的内容。我不明白你所说的“两者都会改变”是什么意思,你能更具体一点吗?你可以通过chang含蓄地传递对两者的引用