Sign in to follow this  

[.net] Immutable objects in c#

This topic is 3663 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

in c++ you can write something like this
#include <map>
using namespace std;

class c
{
public:
const map<int,int>& Get(){return val;}
private:
map<int,int> val;
};

int main()
{
c inst;

inst.Get()[0]=0;//compile-time error, operator[] is not const
};




as i get there is no simple translation of this to c# ?

Share this post


Link to post
Share on other sites
Obviously only works for one collection per object, but allows you to access the object as blah[idx];


public class MyClass {
private Dictionary<string, string> m_MyDict = new Dictionary<string, string>();
public string this[string idx] {
get {
return m_MyDict[idx];
}
set {
throw new InvalidOperationException("Immutable");
}
}
}

Share this post


Link to post
Share on other sites
I think a more accurate translation would be:


public class C
{
private SortedList<int,int> val = new SortedList<int,int>();

public int this[int key]
{
get { return val[key]; }
}

C() {}
}





1. Leaving out the set accessor never even allows you to try to change a value,
Compile time errors are always better than runtime errors.
2. A SortedList captures the same behaviour as std::map ( ordered map of keys to values ), Dictionary is a Hash Table ( which would be std::unordered_map in C++ TR1 )

Share this post


Link to post
Share on other sites
Yes, I am aware that leaving out set causes a compile time error, I left it in as a demonstration. And SortedDictionary would accomplish the same thing, if you wanted to emulate std::map exactly...I rarely have the need to, so std::map -> Dictionary is usually fine for most people.

SortedList uses a little less memory, but SortedDictionary has O(log n) inserts and removes, rather than O(n), so I guess it depends on your usage.

Share this post


Link to post
Share on other sites
Well i think that my example was not completely to the point,
consider the following(c#):



class Symbol
{
//Symbol(Type type, Array array, )
private Type type;
public Symbol(Type ty)
{
type = ty;
}
public Type SymType
{
get
{
return type;
}
}


}

class Type
{
private int val;
private string name;

public int Val
{
get{return val;}
}

public string Name
{
get{return name;}
}
}






now in the client program i can write
Symbol sym=new Symbol(sometype);
Type t=Symbol.SymType;
t.val=88;//type in sym is modified

but the point is that i want user only to see what type is, not modify it.

Share this post


Link to post
Share on other sites
That doesn't work in your example.
Since you made val in Type private and only provided a get accessor, one can't change it.

So

Type t = symbol.SymType
t.Val = xx

will not compile.

But it comes down to one thing, in C++ you specify immutabilty at each point where a class is used, in C# you can only specify it in the definition of a class.

Share this post


Link to post
Share on other sites
Quote:
Original post by BlodBath
Declare the internal data as 'readonly'. This allows you to set it at runtime through the constructor, but makes it immutable from then onwards.


Yah, but then it becomes immutable privately and publicly, which may not be what he wants.

Share this post


Link to post
Share on other sites
Quote:
Original post by GnomeTank
Quote:
Original post by BlodBath
Declare the internal data as 'readonly'. This allows you to set it at runtime through the constructor, but makes it immutable from then onwards.


Yah, but then it becomes immutable privately and publicly, which may not be what he wants.


It seems to satisfy the requirements of the posted examples; if he requires modification and cannot construct a new class to change the value of the readonly field, then I would say C# doesn't provide for that pattern.

Share this post


Link to post
Share on other sites
Well, it seems that I finally came with a most descriptive example of what i want to achieve ))



class Zip
{
public readonly List<int> lst;

public Zip()
{
lst=new List<int>();
}

}

static void Main(string[] args)
{

Zip z = new Zip();

z.lst.Add(99);//i dont want this to compile
}




in cpp if i return 'const list<int>&' i wont be able to modify it, but i guess that it is impossible to achieve in c# without some hacks. Thanks for your replies although.

Share this post


Link to post
Share on other sites
Quote:
Original post by OrangyTang
'const' objects in Java are often achieved by creating an Immutable interface and having the original type implement it. Isn't this possible in C# too?


Yeah, though you pretty much need to re-implement the type as a new type (which makes many things infeasible) to do that.

Share this post


Link to post
Share on other sites
Quote:
Original post by Telastyn
Quote:
Original post by OrangyTang
'const' objects in Java are often achieved by creating an Immutable interface and having the original type implement it. Isn't this possible in C# too?


Yeah, though you pretty much need to re-implement the type as a new type (which makes many things infeasible) to do that.


I'm not sure I follow you.



public interface ImmutablePoint
{
public float getX();
public float getY();
}

public class Point implements ImmutablePoint
{
public float x, y;

public float getX() { return x; }
public float getY() { return y; }
}

main()
{
Point mutable = new Point();
mutable.x = 10; // fine

ImmutablePoint immutable = mutable; // 'const' Point
immutable.x = 10; // compile error
}


Verbose, but it gets the job done. What kind of things does it make infeasible?

Share this post


Link to post
Share on other sites
Quote:

What kind of things does it make infeasible?


The problem is that Point already exists in the standard library. Other parts of the library want to use a Point. Not some new weird Point, but a Point. Which leaves you two options, some sort of implicit conversion which is usually possible, but adds an allocation almost every time you use the class; or inheriting directly from the type, which is 50-50 because a lot of classes are sealed (final) or not structured in a way to be inheritable.


A List for example doesn't have any of its methods listed as virtual, so plan B is right out. Plan A would require a new list copy created every time the conversion occurred (since the user could modify any cached version you give them).

Share this post


Link to post
Share on other sites
Quote:
Original post by Telastyn
Quote:

What kind of things does it make infeasible?


The problem is that Point already exists in the standard library. Other parts of the library want to use a Point. Not some new weird Point, but a Point. Which leaves you two options, some sort of implicit conversion which is usually possible, but adds an allocation almost every time you use the class; or inheriting directly from the type, which is 50-50 because a lot of classes are sealed (final) or not structured in a way to be inheritable.


A List for example doesn't have any of its methods listed as virtual, so plan B is right out. Plan A would require a new list copy created every time the conversion occurred (since the user could modify any cached version you give them).


Ah I see. I usually only have to do this for my own types, so I don't run into those problems. YMMV.

Share this post


Link to post
Share on other sites
Quote:
Original post by OrangyTang
Ah I see. I usually only have to do this for my own types, so I don't run into those problems. YMMV.


Oh yeah, for your own types it's usually not a big deal (unless what you're returning is an event you want to restrict access to...)

Share this post


Link to post
Share on other sites
Here's how you create an immutable (readonly) list :


using System;
using System.Collections.Generic;

namespace X {
class Program {
static void Main(string[] args) {
ImmutableList<int> l = new ImmutableList<int>();
// This can't be called from other consumer assembly
l.list.Add(1);
l.list.Add(2);

// these lines not throw an exception
Console.WriteLine(l[0]);
Console.WriteLine(l[1]);
Console.WriteLine(l.Count);
Console.WriteLine(l.IndexOf(2));

//The list can be enumerated
foreach (int i in l) {
Console.WriteLine(i);
}

// the next lines will throw an exception
try { l.Add(3); } catch (Exception ex) { Console.WriteLine(ex.Message); }
try { l[1] = 4; } catch (Exception ex) { Console.WriteLine(ex.Message); }
try { l.Clear(); } catch (Exception ex) { Console.WriteLine(ex.Message); }

}
}

public class ImmutableList<T> : IList<T>{

internal List<T> list = new List<T>();

public ImmutableList() {
list = new List<T>();
}

#region IList<T> Members

public int IndexOf(T item) {
return list.IndexOf(item);
}

public void Insert(int index, T item) {
throw new NotSupportedException("Operation not supported on immutable type");
}

public void RemoveAt(int index) {
throw new NotSupportedException("Operation not supported on immutable type");
}

public T this[int index] {
get {
return list[index];
}
set {
throw new NotSupportedException("Operation not supported on immutable type");
}
}

#endregion

#region ICollection<T> Members

public void Add(T item) {
throw new NotSupportedException("Operation not supported on immutable type");
}

public void Clear() {
throw new NotSupportedException("Operation not supported on immutable type");
}

public bool Contains(T item) {
return list.Contains(item);
}

public void CopyTo(T[] array, int arrayIndex) {
list.CopyTo(array, arrayIndex);
}

public int Count {
get { return list.Count; }
}

public bool IsReadOnly {
get { return true; }
}

public bool Remove(T item) {
throw new NotSupportedException("Operation not supported on immutable type");
}

#endregion

#region IEnumerable<T> Members

public IEnumerator<T> GetEnumerator() {
return list.GetEnumerator();
}

#endregion

#region IEnumerable Members

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return list.GetEnumerator();
}

#endregion
}

}


Share this post


Link to post
Share on other sites
Quote:

Here's how you create an immutable (readonly) list :

There is already a ReadOnlyCollection<T> that does this, actually. It's in System.Collections.Generic.ObjectModel.

Soth, as per above, your latest example can be written as:

class Zip
{
List<int> lst = new List<int>();

public ReadOnlyCollection<int> List
{
get
{
return (new ReadOnlyCollection<int>(lst));
}
}
}

static void Main(string[] args)
{
Zip z = new Zip();

// This line will no longer compile.
z.List.Add(99);
}

Share this post


Link to post
Share on other sites
Quote:
Original post by jpetrie
Quote:

Here's how you create an immutable (readonly) list :

There is already a ReadOnlyCollection<T> that does this, actually. It's in System.Collections.Generic.ObjectModel.

Soth, as per above, your latest example can be written as:

class Zip
{
List<int> lst = new List<int>();

public ReadOnlyCollection<int> List
{
get
{
return (new ReadOnlyCollection<int>(lst));
}
}
}

static void Main(string[] args)
{
Zip z = new Zip();

// This line will no longer compile.
z.List.Add(99);
}


This example creates a new readlonly list and copies all elements on every property access.
The better way is to create a internal ReadOnlyCollection<T> and return an ICollection<T> but the the standard ReadOnlyCollection<T> is not appropriate for all scenarios as the collection is entirely readonly and in my example the collection is readonly only for public consumers of the assembly.

Share this post


Link to post
Share on other sites

This topic is 3663 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this