【问题标题】:C# List Index Operator doesn't return copy of reference to reference-typeC# 列表索引运算符不返回引用类型的引用副本
【发布时间】:2017-09-30 03:36:04
【问题描述】:

这是我无法理解的事情。我在 C# 中有以下示例代码:

class TestProgram
{
    static void Main()
    {
        var scenario = new Scenario(10);

        var persons = scenario.GetPersons();    //returns copy of reference pointing to internal List
        persons[0] = new Person("Modified");    //why does index operator return actual object, and not a copy of the reference to the object, as below?
        Console.WriteLine(scenario.Persons[0].Name);    //internal List was modified
        Console.WriteLine("Point to same object: " + (persons[0] == scenario.Persons[0]));  //both refs point to the same object

        var pppp = scenario.GetP(); //returns copy of the reference pointing to internal object
        pppp = new Person("Modified");  //overwriting copy of reference has no impact on original object, as expected
        Console.WriteLine(scenario.p.Name);
        Console.WriteLine("Point to same object: " + (pppp == scenario.p));

        var pppp2 = scenario[0]; //returns copy of the reference pointing to internal object
        pppp2 = new Person("Modified"); //overwriting copy of reference has no impact on original object, as expected
        Console.WriteLine(scenario.p.Name);
        Console.WriteLine("Point to same object: " + (pppp2 == scenario.p));

        Console.ReadKey();
    }

}

public class Person
{
    public string Name { get; set; }

    public Person(string name)
    {
        Name = name;
    }
}

public class Scenario
{
    public Person p = new Person("DDD");

    public Person GetP()
    {
        return p;
    }

    public Person this[int index]
    {
        get { return p; }
    }

    public IList<Person> Persons;

    public Scenario(int nrPersons)
    {
        Persons = new List<Person>(nrPersons);
        for(int i = 0; i < nrPersons; i++)
            Persons.Add(new Person("DDD"));
    }

    public IList<Person> GetPersons()
    {
        return Persons;
    }
}

那么,我的问题是,为什么 List 的索引运算符似乎返回了内部对象?我的印象是,方法/重载运算符在传递/返回引用类型时,默认情况下将按值复制并返回它。这意味着应该返回指向同一对象的新引用。与后续 pppp 和 pppp2 引用的情况一样,它的行为符合预期。

我用 dotpeek 查看了 List 的实现,但没有发现任何信息。据我所知,只有从 C# 7.0 开始才支持将 ref 返回到引用类型。

那么这是怎么发生的呢?我错过了什么吗?编译器是否在做一些诡计来实现这一点?因为,我不是在抱怨这种行为。在这种情况下,这似乎是您想要发生的事情。但我只是对不一致的行为感到惊讶。

谢谢。我希望这不是我错过的真正微不足道的东西。

【问题讨论】:

  • 在你的脑海中,尝试将persons[0]替换为persons.Index0,看看你是否能看出persons.Index0 = new Person("Modified");pppp = new Person("Modified");之间的区别,我想它会变得清晰。
  • 是的,你用指向新对象的指针覆盖了指针,而不是对象本身
  • 我想它现在已经清理干净了。我不知道为什么我的印象是 people[0] 将返回(调用索引器的 get)以返回我期望覆盖的副本。但实际上它正在调用索引器的设置器。并传入对新对象的引用。多谢你们!我想我希望它能够像在 C++ 中一样工作(如果我没记错的话,它就是这样工作的,对吧?)
  • C++ 的工作原理完全相同(如果您始终将 C# Person 映射到 C++ 的 Person*
  • @AlexeiLevenkov:我在 C++ 方面不是很有经验,所以也许我遗漏了一些东西,但是下面的代码对我来说表现不同。下标运算符返回对内部对象的引用,而在 C# 中,没有任何内容返回给调用者,而是作为参数接收。现在我看到它,我对其他事情感到困惑。我的印象是 C++ 引用不能指向其他对象,这与指针不同。那么为什么我可以将返回的引用指向下面的 C++ 示例中的新对象:

标签: c# indexing reference return


【解决方案1】:

您所描述的行为 - 传递引用与引用的副本 - 在方法之间传递参数时适用。如果您包含 ref 关键字,您将传递参考。否则默认情况下它是引用的副本。

List 的目的是包含一组对对象的引用并能够维护该组。所以如果你这样做是可以预料的:

persons[0] = new Person("Modified"); 

您正在用新的引用替换之前在该列表中的第一个位置保存的任何引用。

当你这样做时:

var pppp2 = scenario[0];

您正在创建一个变量,该变量包含对Scenario 实例的引用。然后,您可以使用不同的引用(或 null)更新该变量。

实际上,在这两种情况下,您都在做同样的事情。将persons 视为引用的集合,persons[0] 是该集合中的单个引用。无论您更新变量 (pppp2) 还是列表元素 (persons[0]),您都在更新它包含的引用。

【讨论】:

    猜你喜欢
    • 2012-07-07
    • 2020-08-12
    • 1970-01-01
    • 1970-01-01
    • 2012-01-01
    • 1970-01-01
    • 2021-07-04
    • 2020-12-01
    • 2020-07-09
    相关资源
    最近更新 更多