Search code examples
c#linq

C# method returns list by reference instead of by value


I have a class with a static list as shown below:

public class Context
{
    private static readonly List<Definition> definitions;
    
    static Context()
    {
        definitions = LoadXML("path-to-xml-file.xml"));
    }

    public static List<Definition> GetDefinitions()
    {
        return definitions;
    }
}

My problem is making calls to GetDefinitions() seems to return the list by reference instead of by value, because when I do this elsewhere in my code:

var defs = Context.GetDefinitions().ToList();
defs.ForEach(a =>
{
    a.Name = a.Alias ?? a.Name;
});

all subsequent calls to Context.GetDefinitions() will return the modified list - not the original one, hence my conclusion that defs is not a value but a reference to the definitions list.

I tried adding the .ToList() in an attempt to decouple the reference but still I get the same result.

I am also open to a workaround, which allows me to use .Select() instead of .ForEach() in my sample code.


Solution

  • The problem is that the list does not store the items itself, but rather references to the items. Even if you create a new list (e.g. with ToList()), the referenced items stay the same.

    In order to fix this, you need to clone the items in the list so that you have a independent copy of the data. You can implement ICloneable on the items and use return the list like this:

    public static List<Definition> GetDefinitions()
    {
        return definitions.Select(x => (Definition)x.Clone()).ToList();
    }
    

    This way you create a new list that contains the cloned items. However, cloning an item is a tedious task if you need to clone a deeply nested class structure. For a flat structure, using MemberwiseClone is an easy way.