[.net] Immutable objects in c#
'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?
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.
Quote:Original post by TelastynQuote: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?
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).
Quote:Original post by TelastynQuote:
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.
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...)
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 }}
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);}
Quote:Original post by jpetrieQuote:
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.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement