3

I'm following a tutorial on procedurally generated meshes and I am wondering why one can .Add() to a List which is a property of a class when that List only has a get accessor. I'm new to C#, what am I missing here?

For example, the tutorial has the user write this class:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MeshBuilder
{
    private List<Vector3> m_Vertices = new List<Vector3>();
    public List<Vector3> Vertices { get { return m_Vertices; } }

    private List<Vector3> m_Normals = new List<Vector3>();
    public List<Vector3> Normals { get { return m_Normals; } }

    private List<Vector2> m_UVs = new List<Vector2>();
    public List<Vector2> UVs { get { return m_UVs; } }

    private List<int> m_Indices = new List<int>();

    public void AddTriangle(int index0, int index1, int index2)
    {
        m_Indices.Add(index0);
        m_Indices.Add(index1);
        m_Indices.Add(index2);
    }

    public Mesh CreateMesh()
    {
        Mesh mesh = new Mesh();

        mesh.vertices = m_Vertices.ToArray();
        mesh.triangles = m_Indices.ToArray();

        //Normals are optional. Only use them if we have the correct amount:
        if (m_Normals.Count == m_Vertices.Count)
            mesh.normals = m_Normals.ToArray();

        //UVs are optional. Only use them if we have the correct amount:
        if (m_UVs.Count == m_Vertices.Count)
            mesh.uv = m_UVs.ToArray();

        mesh.RecalculateBounds();

        return mesh;
    }
}

Then we write another script which uses the class:

using UnityEngine;
using System.Collections;

public class ProcQuad : MonoBehaviour {

    float m_Width = 1;
    float m_Length = 1;

    // Use this for initialization
    void Start () {

        MeshBuilder meshBuilder = new MeshBuilder();

        //set up vertices and triangles
        meshBuilder.Vertices.Add (new Vector3(0.0f, 0.0f, 0.0f));
        meshBuilder.UVs.Add(new Vector2(0.0f, 0.0f));
        meshBuilder.Normals.Add(Vector3.up);

        meshBuilder.Vertices.Add(new Vector3(0.0f, 0.0f, m_Length));
        meshBuilder.UVs.Add(new Vector2(0.0f, 1.0f));
        meshBuilder.Normals.Add (Vector3.up);

        meshBuilder.Vertices.Add(new Vector3(m_Width, 0.0f, m_Length));
        meshBuilder.UVs.Add(new Vector2(1.0f, 1.0f));
        meshBuilder.Normals.Add(Vector3.up);

        meshBuilder.Vertices.Add (new Vector3(m_Width, 0.0f, 0.0f));
        meshBuilder.UVs.Add(new Vector2(1.0f, 0.0f));
        meshBuilder.Normals.Add(Vector3.up);

        meshBuilder.AddTriangle(0, 1, 2);
        meshBuilder.AddTriangle(0, 2, 3);


        MeshFilter filter = GetComponent<MeshFilter>();

        if (filter != null)
        {
            filter.sharedMesh = meshBuilder.CreateMesh();           
        }

    }

    // Update is called once per frame
    void Update () {

    }
}

So why are we able to perform:

meshBuilder.Vertices.Add (new Vector3(0.0f, 0.0f, 0.0f));

and why does it modify the private field m_Vertices when the property Vertices only has a get accessor?

public List<Vector3> Vertices { get { return m_Vertices; } }

Thanks for your insight.

If anyone is interested in the tutorial, I found it here:

http://jayelinda.com/wp/modelling-by-numbers-part-1a/

Steven
  • 166,672
  • 24
  • 332
  • 435
mgb
  • 33
  • 4

3 Answers3

3

Since the Add method belongs to the List<T> class, all you need to call it is a reference to the list object. It is sufficient to have a getter to supply a reference, because you do not need to assign a new list, only to get an existing one.

If you would like to prohibit adding, change the getter to return AsReadOnly(), like this:

public List<Vector3> Vertices { get { return m_Vertices.AsReadOnly(); } }

Note that the call of Add() would compile, but since at runtime the list is is read-only, an exception would be thrown when the Add() method is called.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • This makes a ton of sense, thanks! I'm choosing yours as the answer because it goes above and beyond by explaining how to prohibit adding. – mgb Oct 04 '13 at 02:25
1

The fact that you can't set the list means that this would give you a compile error:

meshBuilder.Vertices = new List<Vector3>();  //Not allowed! You're assigning to a list that you can't set

But according to the code you have there, it's perfectly legal to get the list m_Vertices and add items to it (just like any other List), because you're not assigning a new List to Vertices.

musical_coder
  • 3,886
  • 3
  • 15
  • 18
1

the get accessor return a reference to the List<T>. After you get a reference to that you can call whatever methods on that List<T>.

The lack of a set accessor prevents you from replacing the List<T> with another List<T>. This is a common practices for List or Collection properties, to make sure that these properties will never return null

Community
  • 1
  • 1
Federico Berasategui
  • 43,562
  • 11
  • 100
  • 154