List<T> Readonly with a Private Set

ListT readonly with a private set

I think you are mixing concepts.

public List<string> myList {get; private set;}

is already "read-only". That is, outside this class, nothing can set myList to a different instance of List<string>

However, if you want a readonly list as in "I don't want people to be able to modify the list contents", then you need to expose a ReadOnlyCollection<string>. You can do this via:

private List<string> actualList = new List<string>();
public ReadOnlyCollection<string> myList
{
get{ return actualList.AsReadOnly();}
}

Note that in the first code snippet, others can manipulate the List, but can not change what list you have. In the second snippet, others will get a read-only list that they cannot modify.

Why a {get;private set} List's content can be changed?

Whenever you return an object from a property or method, the recipient has a reference to that object and can call its properties or methods. If that object has settable properties or methods that change its state, the recipient can modify the object. (Or they could pass the object to something else that modifies it.)

Making set private just means that another class can't set the property to a different list or to null. But when another class gets a reference to that list, it can call the Add, Clear, or any other method.

If you want to protect the contents of your list you could do this:

class Foo
{
private List<Object> _myList;
IReadOnlyList<Object> MyList => _myList.AsReadOnly();
// or IReadOnlyCollection.
// Items in a IReadOnlyList are accessible by index.
}

Internally the class has a list, but other classes can only access a readonly collection of items in the list. They can't modify the collection.

However, by returning a list of objects, the recipient does have access to each of those objects. They don't somehow become "read only." If they have settable properties or methods that change their internal state, they can still be modified.

listT property with private set

Yes, both pieces of code will achieve the same result

//here you are declaring a private field of class
private List<FixedTickProvider> minorTickProviders;
//and only exposing get to rest of the code
public List<FixedTickProvider> MinorTickProviders { get { return minorTickProviders; } }

//here you are declaring a public property which can only be set by the class which is declaring it
public List<FixedTickProvider> MinorTickProviders { get; private set; }

As far as IL is considered there will be slight difference

In case of separate field and property following IL will be generated
Sample Image

In case of single property without backing field

Sample Image

Issue with private set in list properties

When use use a private set what that means is that the property itself is un-setable from outside the class, not that it's methods are not available, and List<T>.Add() is only a method that the compiler knows nothing about.

By example:

public class VehicleContainer{
public List<IVehicle> Vehicles { get; private set; }
...
}
....
VehicleContainer vc = new VehicleContainer();
vc.Vehicles = new List<IVehicle>() // this is an error, because of the private set
int x = vc.Vehicles.Count; // this is legal, property access
vc.Vehicles.Add(new Vehicle()); //this is legal, method call

Take a look at this question, where use of the ReadOnlyCollection class is explained in the case when you want to restrict access to the collection itself, as well as the reference to the collection.

How to make a readonly C# List by non declaring its set attribute

Try:

public List<string> myList {get; private set;}

This will let you set it inside your class, but not outside. Note that this will not stop external clients updating your list, only the reference to it.

Is an explicit readonly property with set & get equivalent to get & private set?

No they are different. The readonly modifier in C# exists typically to mark a field (not property) as readonly. This attribute allows a field value to be set in the constructor of the same class.

The recommended method for a true readonly property is to omit the setter. A private setter simply indicates that the property cannot be set outside of the class.

List with private setter

If you want to use this approach, you would probably want to implement a public Add() method within this class in order to add elements to the underlying private property :

public class Model
{
// Other code omitted for brevity

public bool Add(KeyValuePair<string,int>> item)
{
// Add your item to the underlying list
pair.Add(item);
}
}

Presumably, you were using the results from your Model.Pair and attempting to add to it, which won't work as expected (as Model.Pair would be just returning a copy of your list and not the actual list itself).

Which is better between a readonly modifier and a private setter?

The first one is a read-only field, while the second one gets compiled as a pair of methods (and all reads of the property ProductLocation gets compiled into calls to the corresponding get method and writes to it gets compiled into calls to the set method; internally, these methods will read from / write to an internal, automatically generated, non-read-only field). I'd say the most important difference is thread-safety! (how? read on!)

The basic usage of the class will look exactly the same: code in other classes will only be able to read the value, not change it. Also, the code to read the value will look exactly the same (for example, print(myInstace.ProductLocation); here, you cannot tell how it has been declared, cool, eh?)

The first, most trivial difference is that the property with private setter allows for instances of the same class to modify the value, while in the case of the readonly property, not even the object itself will be able to change the value.

Now, for the thread-safety. The readonly attribute on the field will change its memory visibility semantics when you are working with multiple threads (just like Java's final fields).

A readonly field can only be assigned to at declaration or in the constructor. The value assigned to a readonly field cannot be changed (at least not in a normal way) and it is guaranteed that every thread will see the correctly, initialized value after the constructor returns. Therefore, a readonly field is inherently thread-safe.

To achieve the same thread-safety with the property, you'd have to add some synchronization on your code, which is error-prone. It might lead to dead-locks, data races or reduced performance, depending on the case, and especially if you are not experienced.

So, if the value represents something that semantically cannot be changed after the object's construction, you should not declare a private setter (this would imply that the object might change it). Go for the readonly field (and maybe declare it private and declare a public property with only a getter accessing the field! This is actually the preferred form, since it is not good to expose fields, it is better to only expose methods -- there are many reasons explaining why in this answer)

How to expose read-only collection another type of private collection?

The overhead is not so significant if you consider that ReadOnlyCollection is a wrapper around the list (i.e. it doesn't create a copy of all the items).

In other words, if your class looked like this:

class AnotherClass
{
private ReadOnlyCollection<string> _readonlyList;
public ReadOnlyCollection<string> ReadonlyList
{
get { return _readonlyList; }
}

private List<string> _list;
public List<string> List
{
get { return _list; }
}

public AnotherClass()
{
_list = new List<string>();
_readonlyList = new ReadOnlyCollection<string>(_list);
}
}

Then any change to the List property is reflected in the ReadOnlyList property:

class Program
{
static void Main(string[] args)
{
AnotherClass c = new AnotherClass();

c.List.Add("aaa");
Console.WriteLine(c.ReadonlyList[0]); // prints "aaa"

c.List.Add("bbb");
Console.WriteLine(c.ReadonlyList[1]); // prints "bbb"

Console.Read();
}
}

You may have issues with thread safety, but exposing IEnumerable is even worse for that matter.

Personally, I use a custom IIndexable<T> interface with several handy wrapper classes and extension method that I use all over my code for immutable lists. It allows random access to list elements, and does not expose any methods for modification:

public interface IIndexable<T> : IEnumerable<T>
{
T this[int index] { get; }
int Length { get; }
}

It also allows neat LINQ-like extension methods like Skip, Take and similar, which have better performance compared to LINQ due to the indexing capability.

In that case, you can implement a projection like this:

public class ProjectionIndexable<Tsrc, Ttarget> : IIndexable<Ttarget>
{
public ProjectionIndexable
(IIndexable<Tsrc> src, Func<Tsrc, Ttarget> projection)
{
_src = src;
_projection = projection;
}

#region IIndexable<Ttarget> Members

public Ttarget this[int index]
{
get { return _projection(_src[index]); }
}

public int Length
{
get { return _src.Length; }
}

#endregion

#region IEnumerable<Ttarget> Members

// create your own enumerator here

#endregion
}

And use it like this:

class AnotherClass
{
private IIndexable<string> _readonlyList;
public IIndexable<string> ReadonlyList
{
get { return _readonlyList; }
}

private List<SomeClass> _list;
public List<SomeClass> List
{
get { return _list; }
}

public AnotherClass()
{
_list = new List<SomeClass>();
_readonlyList = new ProjectionIndexable<SomeClass, string>
(_list.AsIndexable(), c => c.Age);
}
}

[Edit]

In the meantime, I posted an article describing such a collection on CodeProject. I saw you've implemented it yourself already, but you can check it out nevertheless and reuse parts of the code where you see fit.



Related Topics



Leave a reply



Submit