I'm basicly trying to make my class able to iterate using foreach. I read this tutorial. MSDN. It seems very straight forward. However, I have a problem when I want to iterate second time. I debugged it; and it turned out that it doesn't call the Reset() function.
Class A
class A : IEnumerable, IEnumerator
{
int[] data = { 0, 1, 2, 3, 4 };
int position = -1;
public object Current
{
get
{
return data[position];
}
}
public bool MoveNext()
{
position++;
return (position < data.Length);
}
public void Reset()
{
position = -1;
}
public IEnumerator GetEnumerator()
{
return (IEnumerator)this;
}
}
When I run the following main function; it never calls Reset() function. So, after one loop I never be able to iterate my class again.
Main
static void Main(string[] args)
{
A a = new A();
foreach (var item in a)
{
Console.WriteLine(item);
}
Console.WriteLine("--- First foreach finished. ---");
foreach (var item in a)
{
Console.WriteLine(item);
}
}
Output:
0
1
2
3
4
--- First foreach finished. ---
Press any key to continue . . .
Any thoughts?
Each time foreach is called, it asks for a new IEnumerator. Returning your class instance is a bad idea - you should make a separate class to implement the IEnumerator, and return it instead.
This is often done by using a nested (private) class, and returning an instance of it. You can pass the class A instance to the private class (giving it access to data), and put the position field in that class. It would allow more than one enumerator to be created simulatenously, and will work properly with subsequent foreach calls.
For example, to modify your code, you'd do something like:
using System;
using System.Collections;
class A : IEnumerable
{
int[] data = { 0, 1, 2, 3, 4 };
public IEnumerator GetEnumerator()
{
return new AEnumerator(this);
}
private class AEnumerator : IEnumerator
{
public AEnumerator(A inst)
{
this.instance = inst;
}
private A instance;
private int position = -1;
public object Current
{
get
{
return instance.data[position];
}
}
public bool MoveNext()
{
position++;
return (position < instance.data.Length);
}
public void Reset()
{
position = -1;
}
}
}
Note that you can also just return the array's enumerator directly (though I was assuming you were trying to learn how to make clean enumerators):
class A : IEnumerable
{
int[] data = { 0, 1, 2, 3, 4 };
public IEnumerator GetEnumerator()
{
return data.GetEnumerator();
}
}
Finally, you can use iterators to implement this in a far simpler manner:
class A : IEnumerable
{
int[] data = { 0, 1, 2, 3, 4 };
public IEnumerator GetEnumerator()
{
for (int i=0;i<data.Length;++i)
yield return data[i];
}
}
That being said, I would strongly recommend implementing IEnumerable<int> in addition to IEnumerable. Generics make this far nicer in terms of usage.
Reset() was basically a mistake. There's already a known method to get a clean enumerator if possible: GetEnumerator().
It is a requirement in the specification that iterator block implementations (for the iterator) throw an exception for this method, hence in the general case it is formally known that it can't be expected to work, and simply: nobody ever calls it. Frankly, they should also have marked it [Obsolete] on the API !
Additionally, many sequences are non-repeatable. Think of iterators sat on a NetworkStream or a random numer generator. Because iterators (in the general case) are not required to be repeatable, you should aim, where possible, to iterate them at most once. Perhaps buffering via ToList() if that is not possible.
"foreach" does not involve Reset() at any point. Just GetEnumerator(), MoveNext(), Current and Dispose().
The enumeration doesn't call Reset. You need to create a new instance of an enumerator, which will likely mean creating a separate class for the enumerator (i.e., not using the same type for the IEnumerable and IEnumerator), like in the code below:
public class StackOverflow_11475328
{
class A : IEnumerable
{
int[] data = { 0, 1, 2, 3, 4 };
public IEnumerator GetEnumerator()
{
return new AEnumerator(this);
}
class AEnumerator : IEnumerator
{
private A parent;
private int position = -1;
public AEnumerator(A parent)
{
this.parent = parent;
}
public object Current
{
get { return parent.data[position]; }
}
public bool MoveNext()
{
position++;
return (position < parent.data.Length);
}
public void Reset()
{
position = -1;
}
}
}
public static void Test()
{
A a = new A();
foreach (var item in a)
{
Console.WriteLine(item);
}
Console.WriteLine("--- First foreach finished. ---");
foreach (var item in a)
{
Console.WriteLine(item);
}
}
}
Both Reed and Carlos are correct. Here is one way you can do this, since int[] implements IEnumerable and IEnumerable<int>
class A : IEnumerable
{
int[] data = { 0, 1, 2, 3, 4 };
public IEnumerator GetEnumerator()
{
return data.GetEnumerator();
}
}
Or, to be more strongly typed, you can use the generic form of IEnumerable:
class A : IEnumerable<int>
{
int[] data = { 0, 1, 2, 3, 4 };
public IEnumerator<int> GetEnumerator()
{
return ((IEnumerable<int>)data).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return data.GetEnumerator();
}
}
Related
I'd like to be able to create an array in C# with an arbitrary range of index bounds, e.g., a 16 element array with indices of 100-115.
Native arrays in C# are 0-based, but I'm told (e.g., in Luaan's comment here), that the C# Array class allows arbitrary lower and upper bounds. But in the examples I've seen elements in the Array class are accessed via myArray.GetValue() and myArray.SetValue() instead of conventional array syntax like myArray [ foo ].
Array arr = Array.CreateInstance(typeof(string), new[]{16}, new[]{100});
Console.WriteLine(arr.Length); // 16
arr.SetValue("foo", 100);
Console.WriteLine(arr.GetValue(100)); // foo
Is there any way to make an array with some arbitrary starting index, like [100] that I can access with traditional [ ] syntax in C#?
You could create a class that implements the decorator pattern: just implement the IList interface (wich is also implemented by Array) and do whatever shifting you want on the this [int index] property.
The decorator pattern is described here:
http://www.codeproject.com/Articles/479635/UnderstandingplusandplusImplementingplusDecoratorp
The Array class does not support this, but you can write your own array class with one-based indices:
public class OneBasedArray<T>
{
public T[] InnerArray;
public T this[int i]
{
get { return InnerArray[i-1]; }
set { InnerArray[i-1] = value; }
}
}
And then use it like this:
var myArray = new OneBasedArray<int> { InnerArray = new int[]{ 1, 2, 3, 4, 5 } };
for(int i = 1; i <=5; i++)
{
Console.WriteLine(myArray[i]);
}
This code is only to get the idea, such a class would of course need a nicer interface.
You can only use an array's indexer when it's a 0-indexed array.
You can use an indexer for a custom, non-array, type, and use whatever logic you want for it, such as making it non-zero-indexed, but that's not an option for arrays.
I think Lukas' answer is probably the easiest way to handle this, but if, for some reason, you really wanted to use Array.CreateInstance (not sure why you would - maybe some external library might insist on it?), you could wrap it in a class like this:
public class NonZeroArray<T>
{
private readonly Array array;
public T this[int i]
{
get { return (T)array.GetValue(i); }
set { array.SetValue(value, i); }
}
public NonZeroArray(int length, int lowerBounds = 0)
{
array = Array.CreateInstance(typeof(T), new int[] { length}, new int[] { lowerBounds } );
}
}
You could obviously make this a lot prettier (and easier to work with) by having it implement the rest of IList<T>. And if you really need the native array, you could implement a property with a getter to expose it when needed.
Using it would be simply:
var myNonZeroArray = new NonZeroArray<string>(16,100);
myNonZeroArray[100] = "foo";
Console.WriteLine(myNonZeroArray[100]); // prints "foo"
Here's a custom class that extends IList<T> to provide functionality for starting at a non-zero index:
public class NonZeroList<T> : IList<T>
{
private int startIndex;
private List<T> inner;
public NonZeroList(int startIndex, IEnumerable<T> content)
{
this.startIndex = startIndex;
inner = content.ToList();
}
public NonZeroList(int startIndex)
{
this.startIndex = startIndex;
inner = new List<T>();
}
public T this[int i]
{
get
{
return inner[i - startIndex];
}
set
{
inner[i - startIndex] = value;
}
}
public IEnumerator<T> GetEnumerator()
{
foreach (T i in inner)
yield return i;
yield break;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return inner.GetEnumerator();
}
public int IndexOf(T item)
{
return inner.IndexOf(item) + startIndex;
}
public void Insert(int index, T item)
{
inner.Insert(index - startIndex, item);
}
public void RemoveAt(int index)
{
inner.RemoveAt(index - startIndex);
}
public void Add(T item)
{
inner.Add(item);
}
public void Clear()
{
inner.Clear();
}
public bool Contains(T item)
{
return inner.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
inner.CopyTo(array, arrayIndex);
}
public int Count
{
get { return inner.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
return inner.Remove(item);
}
}
To use it, you initialize it just as you would a normal List<T>, but with the start index specified (e.g. NonZeroList<int> myList = new NonZeroList<int>(20) { 0, 1, 2, 3 };). You can then use it just as you would a normal List<T> or T[].
If you want to use it as an array instead of a list, then you can simply add bounds checking or implement IEnumerable<T> instead of IList<T>, and create utility functions yourself.
I have been looking for a way of splitting a foreach loop into multiple parts and came across the following code:
foreach(var item in items.Skip(currentPage * itemsPerPage).Take(itemsPerPage))
{
//Do stuff
}
Would items.Skip(currentPage * itemsPerPage).Take(itemsPerPage) be processed in every iteration, or would it be processed once, and have a temporary result used with the foreach loop automatically by the compiler?
No, it would be processed once.
It's the same like:
public IEnumerable<Something> GetData() {
return someData;
}
foreach(var d in GetData()) {
//do something with [d]
}
The foreach construction is equivalent to:
IEnumerator enumerator = myCollection.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
object current = enumerator.Current;
Console.WriteLine(current);
}
}
finally
{
IDisposable e = enumerator as IDisposable;
if (e != null)
{
e.Dispose();
}
}
So, no, myCollection would be processed only once.
Update:
Please note that this depends on the implementation of the IEnumerator that the IEnumerable uses.
In this (evil) example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;
namespace TestStack
{
class EvilEnumerator<T> : IEnumerator<T> {
private IEnumerable<T> enumerable;
private int index = -1;
public EvilEnumerator(IEnumerable<T> e)
{
enumerable = e;
}
#region IEnumerator<T> Membres
public T Current
{
get { return enumerable.ElementAt(index); }
}
#endregion
#region IDisposable Membres
public void Dispose()
{
}
#endregion
#region IEnumerator Membres
object IEnumerator.Current
{
get { return enumerable.ElementAt(index); }
}
public bool MoveNext()
{
index++;
if (index >= enumerable.Count())
return false;
return true;
}
public void Reset()
{
}
#endregion
}
class DemoEnumerable<T> : IEnumerable<T>
{
private IEnumerable<T> enumerable;
public DemoEnumerable(IEnumerable<T> e)
{
enumerable = e;
}
#region IEnumerable<T> Membres
public IEnumerator<T> GetEnumerator()
{
return new EvilEnumerator<T>(enumerable);
}
#endregion
#region IEnumerable Membres
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
}
class Program
{
static void Main(string[] args)
{
IEnumerable<int> numbers = Enumerable.Range(0,100);
DemoEnumerable<int> enumerable = new DemoEnumerable<int>(numbers);
foreach (var item in enumerable)
{
Console.WriteLine(item);
}
}
}
}
Each iteration over enumerable would evaluate numbers two times.
Question:
Would items.Skip(currentPage * itemsPerPage).Take(itemsPerPage) be
processed every iteration, or would it be processed once, and have a
temporary result used with the foreach loop automatically by the
compiler?
Answer:
It would be processed once, not every iteration. You can put the collection into a variable to make the foreach more readable. Illustrated below.
foreach(var item in items.Skip(currentPage * itemsPerPage).Take(itemsPerPage))
{
//Do stuff
}
vs.
List<MyClass> query = items.Skip(currentPage * itemsPerPage).Take(itemsPerPage).ToList();
foreach(var item in query)
{
//Do stuff
}
vs.
IEnumerable<MyClass> query = items.Skip(currentPage * itemsPerPage).Take(itemsPerPage);
foreach(var item in query)
{
//Do stuff
}
The code that you present will only iterate the items in the list once, as others have pointed out.
However, that only gives you the items for one page. If you are handling multiple pages, you must be calling that code once for each page (because somewhere you must be incrementing currentPage, right?).
What I mean is that you must be doing something like this:
for (int currentPage = 0; currentPage < numPages; ++currentPage)
{
foreach (var item in items.Skip(currentPage*itemsPerPage).Take(itemsPerPage))
{
//Do stuff
}
}
Now if you do that, then you will be iterating the sequence multiple times - once for each page. The first iteration will only go as far as the end of the first page, but the next will iterate from the beginning to the end of the second page (via the Skip() and the Take()) - and the next will iterate from the beginning to the end of the third page. And so on.
To avoid that you can write an extension method for IEnumerable<T> which partitions the data into batches (which you could also describe as "paginating" the data into "pages").
Rather than just presenting an IEnumerable of IEnumerables, it can be more useful to wrap each batch in a class to supply the batch index along with the items in the batch, like so:
public sealed class Batch<T>
{
public readonly int Index;
public readonly IEnumerable<T> Items;
public Batch(int index, IEnumerable<T> items)
{
Index = index;
Items = items;
}
}
public static class EnumerableExt
{
// Note: Not threadsafe, so not suitable for use with Parallel.Foreach() or IEnumerable.AsParallel()
public static IEnumerable<Batch<T>> Partition<T>(this IEnumerable<T> input, int batchSize)
{
var enumerator = input.GetEnumerator();
int index = 0;
while (enumerator.MoveNext())
yield return new Batch<T>(index++, nextBatch(enumerator, batchSize));
}
private static IEnumerable<T> nextBatch<T>(IEnumerator<T> enumerator, int blockSize)
{
do { yield return enumerator.Current; }
while (--blockSize > 0 && enumerator.MoveNext());
}
}
This extension method does not buffer the data, and it only iterates through it once.
Given this extension method, it becomes more readable to batch up the items. Note that this example enumerates through ALL items for all pages, unlike the OP's example which only iterates through the items for one page:
var items = Enumerable.Range(10, 50); // Pretend we have 50 items.
int itemsPerPage = 20;
foreach (var page in items.Partition(itemsPerPage))
{
Console.Write("Page " + page.Index + " items: ");
foreach (var i in page.Items)
Console.Write(i + " ");
Console.WriteLine();
}
Is there anyway to foreach through a list from the end to the beginning rather than the beginning to then end (preferably without reordering the list).
using System.Linq;
foreach(var item in source.Reverse())
{
...
}
Edit: There is one more step if you are dealing specifically with a List<T>. That class defines its own Reverse method whose signature is not the same as the Enumerable.Reverse extension method. In that case, you need to "lift" the variable reference to IEnumerable<T>:
using System.Linq;
foreach(var item in list.AsEnumerable().Reverse())
{
...
}
you could use a regular for loop, start at the end and decrement, instead of starting at the top and incrementing.
something like:
for(int i=foo.lenth; i != 0; i--)
{
do stuff
}
You probably don't want to do anything complicated, so I would suggest just using a for loop.
However, if it were somehow a requirement, you can certainly implement your own iterators for custom list iteration behavior.
It depends on what you mean by list.
List<T> ? No, unless you use Linq and it's Reverse() function.
Your custom collection? Easily, just implement IEnumerator like you
want.
Error checking ommitted for clarity. Use a custom implementation of IEnumerable and IEnumerator. This will avoid unnecessary copying.
using System;
using System.Collections.Generic;
namespace ConsoleApplication3
{
class ReversedEnumerator : IEnumerator<int>
{
List<int> v;
int index;
public ReversedEnumerator(List<int> v) {
this.v = v;
this.index = v.Count;
}
public int Current
{
get { return v[index]; }
}
public void Dispose()
{
}
object System.Collections.IEnumerator.Current
{
get { return v[index]; }
}
public bool MoveNext()
{
return --index >= 0;
}
public void Reset()
{
index = this.v.Count;
}
}
class EnumeratorStub : IEnumerable<int>
{
List<int> v;
public EnumeratorStub(List<int> v)
{
this.v = v;
}
public IEnumerator<int> GetEnumerator()
{
return new ReversedEnumerator(v);
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return new ReversedEnumerator(v);
}
}
class Program
{
static EnumeratorStub Reverse(List<int> v)
{
return new EnumeratorStub(v);
}
static void Main(string[] args)
{
List<int> v = new List<int>();
v.Add(1);
v.Add(2);
v.Add(3);
foreach (int item in Reverse(v))
{
Console.WriteLine(item);
}
Console.ReadKey();
}
}
}
I would recommend to refactor the code sample to use generics. That way you could use this for any container type.
IList<String> strList = new IList<String>();
strList.Add("A");
strList.Add("B");
strList.Add("C");
for (int i = strList.Count-1; i>=0;i--)
{
Console.WriteLine(strList[i]);
}
not tried but should work.
not c# but you can do it too :-)
Dim a As New List(Of Integer)
a.Add(1)
a.Add(2)
a.Add(3)
For Each i In a.AsEnumerable.Reverse
Debug.Print(i)
Next
You can construct your list as a stack and then iterate over the stack:
Stack<char> stack = new Stack<char>();
//Add items...
foreach(var item in stack)
{
...
}
I’m trying to develop a type to track the current iteration position with a list.
I ideally want to used it with a foreach loop using the IEnumerable interface but the interface has no start/stop events or method to hooking to reset the count.
Currently I have created a GetNext( ) method which return the next value in the list and increments a count by 1.
Does anyone know I can achieve the same functionality using IEnumerable so I can use the type with a foreach loop?
So for example; imagine a list contains 10 items. One method could iterate an instance of the type to position 4 then method two would iterate the same instance starting at position 5 to 6 then method 3 would iterate the remainng from position 7 to 10 – so the type instance tracks the current position.
Any ideas are greatly appreciated (code shown below) . Thanks
public sealed class PositionTracker<T> : IEnumerable
{
private readonly object _syncLock = new object();
private readonly IList<T> _list = new List<T>();
private int _current;
public PositionTracker(IList<T> list)
{
_list = list;
}
public T GetCurrent()
{
lock (_syncLock)
{
return _list[_current];
}
}
public T GetNext()
{
lock (_syncLock)
{
T t = GetCurrent();
if (_current < _list.Count - 1)
{
_current++;
}
return t;
}
}
public IEnumerator<T> GetEnumerator()
{
lock (_syncLock)
{
return _list.GetEnumerator();
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Reset()
{
lock (_syncLock)
{
_current = 0;
}
}
public int Count
{
get
{
lock (_syncLock)
{
return _list.Count;
}
}
}
}
[TestFixture]
public class PositionTrackerTests
{
[Test]
public void Position_CurrentPosition_Test()
{
List<string> list = new List<string>(new string[] { "A", "B", "C", "D" });
PositionTracker<string> positionTracker = new PositionTracker<string>(list);
Assert.IsTrue(positionTracker.GetNext().Equals("A"));
Assert.IsTrue(positionTracker.GetNext().Equals("B"));
Assert.IsTrue(positionTracker.GetNext().Equals("C"));
Assert.IsTrue(positionTracker.GetNext().Equals("D"));
}
}
Have a look at yield keyword. Especially this link of Chapter 6 of the Book 'C# in Depth' By Jon Skeet
P.S. I hope you are doing it in C#.NET 2.0+
Check this link: foreach with generic List, detecting first iteration when using value type
There is a link to a SmartEnumerable class by Jon Skeet. It is basically a wrapper for IEnumerable, which gives you a public SmartEnumerable<string>.Entry class which contains the item's index.
Also, nothing stops you from doing this:
public class MyClass
{
private List<String> list = new List<String>() { "1", "2", "3", "4", "5" }
public IEnumerable<String> GetItems(int from, int to)
{
for (int i=from; i<to; i++)
yield return list[i];
}
}
You can achieve most of this with an extension method, and overloads of Enumerable.Select and Where.
Both Select and Where have overloads where the delegate is passed both the item and its index:
var input = new[]{'a','b','c','d'};
var indexed = input.Select((v,i) => new { Value = v, Index = i });
foreach (var v in indexed) {
Console.WriteLine("Index #{0} is '{1}'", v.Index, v.Value);
}
To trigger delegates before the first and after the last items (but only if there is at least one item):
public static IEnumerable<T> StartAndEnd<T>(this IEnumerable<T> input,
Action onFirst,
Action onLast) {
var e = input.GetEnumerator();
if (!e.MoveNext()) { yield break; }
onFirst();
do {
yield return e.Current;
} while (e.MoveNext());
onLast();
}
and then use it as:
var input = new[]{'a','b','c','d'};
var indexed = input.StartAndEnd(() => { Console.WriteLine("First!");},
() => { Console.WriteLine("Last!");})
.Select((v,i) => new { Value = v, Index = i });
foreach (var v in indexed) {
Console.WriteLine("Index #{0} is '{1}'", v.Index, v.Value);
}
which gives the result:
First!
Index #0 is 'a'
Index #1 is 'b'
Index #2 is 'c'
Index #3 is 'd'
Last!
The delegates could set a local (by forming a closure) which is checked in a loop.
A more sophisticated version could call the onLast delegate before the last element of the sequence, but this would require buffering an element of the sequence to detect end before yielding that element.
When I want to make a value type read-only outside of my class I do this:
public class myClassInt
{
private int m_i;
public int i {
get { return m_i; }
}
public myClassInt(int i)
{
m_i = i;
}
}
What can I do to make a List<T> type readonly (so they can't add/remove elements to/from it) outside of my class? Now I just declare it public:
public class myClassList
{
public List<int> li;
public myClassList()
{
li = new List<int>();
li.Add(1);
li.Add(2);
li.Add(3);
}
}
You can expose it AsReadOnly. That is, return a read-only IList<T> wrapper. For example ...
public ReadOnlyCollection<int> List
{
get { return _lst.AsReadOnly(); }
}
Just returning an IEnumerable<T> is not sufficient. For example ...
void Main()
{
var el = new ExposeList();
var lst = el.ListEnumerator;
var oops = (IList<int>)lst;
oops.Add( 4 ); // mutates list
var rol = el.ReadOnly;
var oops2 = (IList<int>)rol;
oops2.Add( 5 ); // raises exception
}
class ExposeList
{
private List<int> _lst = new List<int>() { 1, 2, 3 };
public IEnumerable<int> ListEnumerator
{
get { return _lst; }
}
public ReadOnlyCollection<int> ReadOnly
{
get { return _lst.AsReadOnly(); }
}
}
Steve's answer also has a clever way to avoid the cast.
There is limited value in attempting to hide information to such an extent. The type of the property should tell users what they're allowed to do with it. If a user decides they want to abuse your API, they will find a way. Blocking them from casting doesn't stop them:
public static class Circumventions
{
public static IList<T> AsWritable<T>(this IEnumerable<T> source)
{
return source.GetType()
.GetFields(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance)
.Select(f => f.GetValue(source))
.OfType<IList<T>>()
.First();
}
}
With that one method, we can circumvent the three answers given on this question so far:
List<int> a = new List<int> {1, 2, 3, 4, 5};
IList<int> b = a.AsReadOnly(); // block modification...
IList<int> c = b.AsWritable(); // ... but unblock it again
c.Add(6);
Debug.Assert(a.Count == 6); // we've modified the original
IEnumerable<int> d = a.Select(x => x); // okay, try this...
IList<int> e = d.AsWritable(); // no, can still get round it
e.Add(7);
Debug.Assert(a.Count == 7); // modified original again
Also:
public static class AlexeyR
{
public static IEnumerable<T> AsReallyReadOnly<T>(this IEnumerable<T> source)
{
foreach (T t in source) yield return t;
}
}
IEnumerable<int> f = a.AsReallyReadOnly(); // really?
IList<int> g = f.AsWritable(); // apparently not!
g.Add(8);
Debug.Assert(a.Count == 8); // modified original again
To reiterate... this kind of "arms race" can go on for as long as you like!
The only way to stop this is to completely break the link with the source list, which means you have to make a complete copy of the original list. This is what the BCL does when it returns arrays. The downside of this is that you are imposing a potentially large cost on 99.9% of your users every time they want readonly access to some data, because you are worried about the hackery of 00.1% of users.
Or you could just refuse to support uses of your API that circumvent the static type system.
If you want a property to return a read-only list with random access, return something that implements:
public interface IReadOnlyList<T> : IEnumerable<T>
{
int Count { get; }
T this[int index] { get; }
}
If (as is much more common) it only needs to be enumerable sequentially, just return IEnumerable:
public class MyClassList
{
private List<int> li = new List<int> { 1, 2, 3 };
public IEnumerable<int> MyList
{
get { return li; }
}
}
UPDATE Since I wrote this answer, C# 4.0 came out, so the above IReadOnlyList interface can take advantage of covariance:
public interface IReadOnlyList<out T>
And now .NET 4.5 has arrived and it has... guess what...
IReadOnlyList interface
So if you want to create a self-documenting API with a property that holds a read-only list, the answer is in the framework.
JP's answer regarding returning IEnumerable<int> is correct (you can down-cast to a list), but here is a technique that prevents the down-cast.
class ExposeList
{
private List<int> _lst = new List<int>() { 1, 2, 3 };
public IEnumerable<int> ListEnumerator
{
get { return _lst.Select(x => x); } // Identity transformation.
}
public ReadOnlyCollection<int> ReadOnly
{
get { return _lst.AsReadOnly(); }
}
}
The identity transformation during enumeration effectively creates a compiler-generated iterator - a new type which is not related to _lst in any way.
Eric Lippert has a series of articles on Immutability In C# on his blog.
The first article in the series can be found here.
You might also find useful Jon Skeet's answer to a similar question.
public List<int> li;
Don't declare public fields, it's generally considered bad practice... wrap it in a property instead.
You can expose your collection as a ReadOnlyCollection :
private List<int> li;
public ReadOnlyCollection<int> List
{
get { return li.AsReadOnly(); }
}
public class MyClassList
{
private List<int> _lst = new List<int>() { 1, 2, 3 };
public IEnumerable<int> ListEnumerator
{
get { return _lst.AsReadOnly(); }
}
}
To check it
MyClassList myClassList = new MyClassList();
var lst= (IList<int>)myClassList.ListEnumerator ;
lst.Add(4); //At this point ypu will get exception Collection is read-only.
public static IEnumerable<T> AsReallyReadOnly<T>(this IEnumerable<T> source)
{
foreach (T t in source) yield return t;
}
if I add to Earwicker's example
...
IEnumerable<int> f = a.AsReallyReadOnly();
IList<int> g = f.AsWritable(); // finally can't get around it
g.Add(8);
Debug.Assert(a.Count == 78);
I get InvalidOperationException: Sequence contains no matching element.