Immutable List Class?

I’m trying to code something where I have a class with a private list of objects and don’t want that list to be modifiable outside the class, but I do want to be able to view its contents from outside the class.

Namely I want to use it in a foreach loop (which means that any getter function needs to return something that implements IEnumerable<T> so that foreach can invoke GetEnumerator() and I can’t return the Enumerator itself.

That is, I want an ImmutableList, which does not appear to exist in Unity’s version of Mono .NET, the entire System.Collections.Immutable namespace is missing.

We have submitted a fix for the documentation. Thanks for pointing that out.

3 Answers

3

Hi all.
Just to mention that - as of today - .NET library includes a IReadOnlyList generic interface, that can be used in a getter to expose a (private) mutable List:

public class SensorState
{
    readonly List<double> temperatureReadings;

    public IReadOnlyList<double> TemperatureReadings => temperatureReadings;

    public SensorState()
    {
        temperatureReadings = new List<double>();
    }

    public void RecordTemperature(double temperature)
    {
        temperatureReadings.Add(temperature);
    }
}

What I don't like about this approach is that - You can still cast the IReadOnlyList back to List - You will be stuck with boxed enumerators, allocating garbage It is fairly easy to write a custom generic ReadonlyList struct that encapsulates the original list in a private field and exposes all read-only properties and methods, including a struct Enumerator.

you're right on being careful about the potential impact of enumerators and allocations, it depends a lot on how frequently this data structure is traversed. About the cast, I'd say it's as if you (or your library/code user) is hacking the code. I mean, that interface is a contract, code users are supposed to use it as provided, otherwise they are on their own, good luck breaking things.

Could create a wrapper class that has 1 field being a List. This will eliminate any methods from being used outside the object that you don’t want to be used.

Only issue is being required to make the wrapper class iterable and read the elements of the list inside as well as making it Generic so any Type can be given.

I certainly could do that. I was just looking to see if anything was already supplied. Thanks.

The other issue is that it's not a persistent data structure.

Hi @Draco18s

Could this work for you? By using getter, you can get a read only copy of your local List, which you can still modify as usual.

using UnityEngine;
using System.Collections.Generic;
using System.Collections.ObjectModel;
  
public class FooBar : MonoBehaviour 
{
    // Your list
    [SerializeField]
    private List<GameObject> myList = new List<GameObject>();

    // Getter for read only list
    public ReadOnlyCollection<GameObject> MyList
    {
        get
        {
            return myList.AsReadOnly();
        }
    }
  
    void Start () 
    {
        // TEST -----------------------------
          
        var obj = new GameObject();
        obj.name = "obj1";
          
        var obj2 = new GameObject();
        obj2.name = "obj2";
          
        myList.Add(obj);
        myList.Add(obj2);
          
        // Get as readonly
        var readList = MyList;
          
        // Count
        Debug.Log("MyList Length: " + MyList.Count);
          
        // Can read but can't remove
        if (readList.Contains(obj))
        Debug.Log("list has obj");
         
        var o = readList[1];
        Debug.Log("list item 1 is:" + o.name);

        // Foreach
        foreach (var item in readList)
        {
            Debug.Log("foreach, item is: " + item.name.ToString());
        }
    }
}

This does work, though I'm not sure it would have worked back when I asked. Not sure and can't check. Thanks though!

We are working on an NDK upgrade now. I'm not sure what Unity release will have it, but I can say that it won't make the Unity 2018.2 release. It will be sometime after that.