C#List Index Operator不会将引用副本返回给reference-type

时间:2017-05-02 14:44:09

标签: c# indexing reference return

这是我无法理解的事情。我在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开始支持。

那么这是怎么发生的?我错过了什么吗?编译器是否正在做一些技巧来实现这一目标?因为,我不是在抱怨这种行为。看起来这就是你想要在这种情况下发生的事情。但我对这种不一致的行为感到惊讶。

感谢。我希望我失踪并不是真正微不足道的事情。

1 个答案:

答案 0 :(得分:0)

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

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

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

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

执行此操作时:

var pppp2 = scenario[0];

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

更新该变量

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