为什么在没有引用的情况下传递列表到函数时,其行为就像使用引用传递一样?

发布于 2024-12-02 23:13:02 字数 782 浏览 2 评论 0原文

如果我没有犯下严重的错误,这种行为对我来说很奇怪。我将在下面发布一个示例代码,而不是解释,请告诉我为什么我得到输出 x 而不是 y。

    private void button1_Click(object sender, EventArgs e)
    {
        List<int> l = new List<int>() { 1, 2, 3 };
        Fuss(l);
        MessageBox.Show(l.Count.ToString()); // output is 5
    }

    private void Fuss(List<int> l)
    {
        l.Add(4);
        l.Add(5);
    }

我假设输出应该是 3。但是我得到的输出是 5。我知道如果我这样做,输出可以是 5:

    private void button1_Click(object sender, EventArgs e)
    {
        List<int> l = new List<int>() { 1, 2, 3 };
        Fuss(ref l);
        MessageBox.Show(l.Count.ToString()); // output is 5
    }

    private void Fuss(ref List<int> l)
    {
        l.Add(4);
        l.Add(5);
    }

If I did not get this terribly wrong, this behaviour is strange for me. Rather than explaining, I'll post a sample code below and please tell me why does I get output x and not y.

    private void button1_Click(object sender, EventArgs e)
    {
        List<int> l = new List<int>() { 1, 2, 3 };
        Fuss(l);
        MessageBox.Show(l.Count.ToString()); // output is 5
    }

    private void Fuss(List<int> l)
    {
        l.Add(4);
        l.Add(5);
    }

Output should, I assume would be 3. But I get the output as 5. I understand the output can be 5 if I do this:

    private void button1_Click(object sender, EventArgs e)
    {
        List<int> l = new List<int>() { 1, 2, 3 };
        Fuss(ref l);
        MessageBox.Show(l.Count.ToString()); // output is 5
    }

    private void Fuss(ref List<int> l)
    {
        l.Add(4);
        l.Add(5);
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

挽袖吟 2024-12-09 23:13:02

它的行为不像是通过引用传递的。

void ChangeMe(List<int> list) {
  list = new List<int>();
  list.Add(10);
}
void ChangeMeReally(ref List<int> list) {
  list = new List<int>();
  list.Add(10);
}

尝试一下。你注意到其中的区别了吗?

如果您在没有引用的情况下传递列表(或任何引用类型),则只能更改列表的内容(因为正如其他人所说,您正在传递对堆上对象的引用,从而更改相同的“内存”)。

但是您不能更改“list”,“list”是一个指向 List 类型的对象的变量。如果您通过引用传递“列表”(以使其指向其他位置),则只能更改“列表”。您将获得引用的副本,如果更改,则只能在您的方法内观察到该副本。

It does not act like its passed by ref.

void ChangeMe(List<int> list) {
  list = new List<int>();
  list.Add(10);
}
void ChangeMeReally(ref List<int> list) {
  list = new List<int>();
  list.Add(10);
}

Try it. Do you notice the difference?

You can only change the contents of list (or any reference type) if you pass it without a ref (because as others have said, you are passing a reference to the object on the heap and thus change the same "memory").

However you cannot change "list", "list" is a variable that points to an object of type List. You can only change "list" if you pass it by reference (to make it point somewhere else). You get a copy of the reference, which if changed, can only be observed inside your method.

绾颜 2024-12-09 23:13:02

在 C# 中,参数按值传递,除非它们用 refout 修饰符标记。对于引用类型,这意味着引用是按值传递的。因此,在 Fuss 中,l 引用 List 与其调用者相同的实例。因此,对此 List 实例的任何修改都将被调用者看到。

现在,如果您使用 refout 标记参数 l,则该参数将通过引用传递。这意味着在 Fuss 中,l 是存储位置的别名,用作调用该方法的参数。需要明确的是:

public void Fuss(ref List<int> l)

Fuss 中,由 Now 调用

List<int> list = new List<int> { 1, 2, 3 };
Fuss(list);

llist 的别名。特别是,如果您将 List 的新实例分配给 l,调用者将看到分配给变量 list 的新实例以及。特别是,如果您说

public void Fuss(ref List<int> l) {
    l = new List<int> { 1 };
}

then,调用者现在将看到一个包含一个元素的列表。但是,如果您说

public void Fuss(List<int> l) {
    l = new List<int> { 1 };
}

并调用

List<int> list = new List<int> { 1, 2, 3 };
Fuss(list);

,那么调用者仍然会看到 list 具有三个元素。

清除?

Parameters are passed by value in C# unless they are marked with the ref or out modifiers. For reference types, this means that the reference is passed by value. Therefore, in Fuss, l is referring to the same instance of List<int> as its caller. Therefore, any modifications to this instance of List<int> will be seen by the caller.

Now, if you mark the parameter l with ref or out, then the parameter is passed by reference. What this means is that in Fuss, l is an alias for storage location used as a parameter to invoke the method. To be clear:

public void Fuss(ref List<int> l)

called by

List<int> list = new List<int> { 1, 2, 3 };
Fuss(list);

Now, in Fuss, l is an alias for list. In particular, if you assign a new instance of List<int> to l, the caller will see that new instance assigned to the variable list as well. In particular, if you say

public void Fuss(ref List<int> l) {
    l = new List<int> { 1 };
}

then the caller will now see a list with one element. But if you say

public void Fuss(List<int> l) {
    l = new List<int> { 1 };
}

and call by

List<int> list = new List<int> { 1, 2, 3 };
Fuss(list);

then the caller will still see list as having three elements.

Clear?

梦里兽 2024-12-09 23:13:02

ByRef 和 ByVal 仅适用于值类型,不适用于引用类型,引用类型始终像“byref”一样传递。

如果您需要谨慎地修改列表,请使用“.ToList()”函数,您将获得列表的克隆。

请记住,如果您的列表包含引用类型,则“新”列表包含指向与原始列表相同的对象的指针。

ByRef and ByVal only apply to value types, not to reference types, which are always passed as though they were "byref".

If you need to modify a list discreetly, use the ".ToList()" function, and you'll get a clone of your list.

Keep in mind that if your list contains reference types, your "new" list contains pointers to the same objects that your original list did.

久隐师 2024-12-09 23:13:02

列表已经是引用类型,因此当您将它们传递给方法时,您正在传递引用。任何 Add 调用都会影响调用者中的列表。

通过 ref 传递 List 的行为本质上类似于向该列表传递双指针。这是一个例子:

using System;
using System.Collections.Generic;

public class Test
{
    public static void Main()
    {
        List<int> l = new List<int>() { 1, 2, 3 };
        Fuss(l);
        Console.WriteLine(l.Count); // Count will now be 5.

        FussNonRef(l);
        Console.WriteLine(l.Count); // Count will still be 5 because we 
                                    // overwrote the copy of our reference 
                                    // in FussNonRef.

        FussRef(ref l);
        Console.WriteLine(l.Count); // Count will be 2 because we changed
                                    // our reference in FussRef.
    }

    private static void Fuss(List<int> l)
    {
        l.Add(4);
        l.Add(5);
    }

    private static void FussNonRef(List<int> l)
    {
        l = new List<int>();
        l.Add(6);
        l.Add(7);
    }

    private static void FussRef(ref List<int> l)
    {
        l = new List<int>();
        l.Add(8);
        l.Add(9);
    }
}

Lists are already reference types, so when you pass them to a method, you are passing a reference. Any Add calls will affect the list in the caller.

Passing a List<T> by ref behaves essentially like passing a double-pointer to that list. Here's an illustration:

using System;
using System.Collections.Generic;

public class Test
{
    public static void Main()
    {
        List<int> l = new List<int>() { 1, 2, 3 };
        Fuss(l);
        Console.WriteLine(l.Count); // Count will now be 5.

        FussNonRef(l);
        Console.WriteLine(l.Count); // Count will still be 5 because we 
                                    // overwrote the copy of our reference 
                                    // in FussNonRef.

        FussRef(ref l);
        Console.WriteLine(l.Count); // Count will be 2 because we changed
                                    // our reference in FussRef.
    }

    private static void Fuss(List<int> l)
    {
        l.Add(4);
        l.Add(5);
    }

    private static void FussNonRef(List<int> l)
    {
        l = new List<int>();
        l.Add(6);
        l.Add(7);
    }

    private static void FussRef(ref List<int> l)
    {
        l = new List<int>();
        l.Add(8);
        l.Add(9);
    }
}
缱绻入梦 2024-12-09 23:13:02

“List”类型的变量、参数或字段或任何其他引用类型实际上并不保存列表(或任何其他类的对象)。相反,它会保存类似“对象 ID #29115”的内容(当然,不是这样一个实际的字符串,而是本质上意味着这一点的位组合)。在其他地方,系统将有一个称为堆的索引对象集合;如果 List 类型的某个变量保存“对象 ID #29115”,则堆中的对象 #29115 将是 List 的实例或从中派生的某种类型。

如果 MyFoo 是 List 类型的变量,像 'MyFoo.Add("George")' 这样的语句实际上不会改变 MyFoo;相反,它的意思是“检查存储在 MyFoo 中的对象 ID,并调用存储在其中的对象的“Add”方法。如果 MyFoo 在执行语句之前持有“Object ID #19533”,则之后它将继续这样做,但是 Object ID #19533 将调用其 Add 方法(可能会更改该对象),相反,像“MyFoo = MyBar”这样的语句将使 MyFoo 拥有与 MyBar 相同的对象 ID,但实际上不会这样做。如果 MyBar 在该语句之前持有“Object ID #59212”,则在该语句之后,MyFoo 也将持有“ObjectId #59212”,对象 ID #19533 和对象 ID#59212 都不会发生任何情况。 。

A variable, parameter, or field of type "List", or any other reference type, doesn't actually hold a list (or object of any other class). Instead, it will hold something like "Object ID #29115" (not such an actual string, of course, but a combination of bits which means essentially that). Elsewhere, the system will have an indexed collection of objects called the heap; if some variable of type List holds "Object ID #29115", then object #29115 in the heap will be an instance of List or some type derived therefrom.

If MyFoo is a variable of type List, a statement like 'MyFoo.Add("George")' won't actually change MyFoo; instead, it means "Examine the object ID stored in MyFoo, and invoke the "Add" method of the object stored therein. If MyFoo held "Object ID #19533" before the statement executed, it will continue to do so afterward, but Object ID #19533 will have had its Add method invoked (probably altering that object). Conversely, a statement like "MyFoo = MyBar" will make MyFoo hold the same object-id as MyBar, but won't actually do anything to the objects in question. If MyBar held "Object ID #59212" before the statement, then after the statement, MyFoo will also hold "ObjectId #59212". Nothing will have happened to object ID #19533, nor object ID#59212.

樱花细雨 2024-12-09 23:13:02

对于像 List 这样的引用类型,ref 和 non-ref 之间的区别不在于是否传递引用(这种情况总是发生),而是该引用是否可以更改。尝试以下操作

private void Fuss(ref List<int> l)
{
    l = new List<int> { 4, 5 };
}

,您将看到计数为 2,因为该函数不仅操作原始列表,还操作引用本身。

The difference between ref and non-ref for reference types like List is not whether you pass a reference (that happens always), but whether that reference can be changed. Try the following

private void Fuss(ref List<int> l)
{
    l = new List<int> { 4, 5 };
}

and you'll see the count is 2, because the function not only manipulated the original list but the reference itself.

雪花飘飘的天空 2024-12-09 23:13:02

只有 int、double 等基本类型是按值传递的。

复杂类型(如列表)通过引用传递(确切地说,这是按值传递指针)。

Only primitive types like int, double etc. are passed by value.

Complex types (like list) are passed via reference (which is a passing pointer by value, to be exact).

抱着落日 2024-12-09 23:13:02

让我们解释一下简单:)

  • 第一个代码块中的“Fuss”知道他应该接受来自列表类型的引用
  • 第二个代码块中的“Fuss”知道他应该接受不接受任何东西!但只是对“sth else”的引用,意外地这个“sth else”与你的第一块代码中的东西是一样的

lets explain it Easy :)

  • "Fuss" in your first chunk of code knows that he should accept a refrence from a list type
  • "Fuss" in your second chunk of code knows that he should accept nothing! but just a refrence to "sth else" which accidently this "sth else" is the same thing which was in your first chunk of code ????♥

if it seems confusing i think you should have a deep understanding of what a reference is

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文