ReadOnlyCollection or IEnumerable for exposing member collections? - c#

Is there any reason to expose an internal collection as a ReadOnlyCollection rather than an IEnumerable if the calling code only iterates over the collection?
class Bar
{
private ICollection<Foo> foos;
// Which one is to be preferred?
public IEnumerable<Foo> Foos { ... }
public ReadOnlyCollection<Foo> Foos { ... }
}
// Calling code:
foreach (var f in bar.Foos)
DoSomething(f);
As I see it IEnumerable is a subset of the interface of ReadOnlyCollection and it does not allow the user to modify the collection. So if the IEnumberable interface is enough then that is the one to use. Is that a proper way of reasoning about it or am I missing something?
Thanks /Erik

More modern solution
Unless you need the internal collection to be mutable, you could use the System.Collections.Immutable package, change your field type to be an immutable collection, and then expose that directly - assuming Foo itself is immutable, of course.
Updated answer to address the question more directly
Is there any reason to expose an internal collection as a ReadOnlyCollection rather than an IEnumerable if the calling code only iterates over the collection?
It depends on how much you trust the calling code. If you're in complete control over everything that will ever call this member and you guarantee that no code will ever use:
ICollection<Foo> evil = (ICollection<Foo>) bar.Foos;
evil.Add(...);
then sure, no harm will be done if you just return the collection directly. I generally try to be a bit more paranoid than that though.
Likewise, as you say: if you only need IEnumerable<T>, then why tie yourself to anything stronger?
Original answer
If you're using .NET 3.5, you can avoid making a copy and avoid the simple cast by using a simple call to Skip:
public IEnumerable<Foo> Foos {
get { return foos.Skip(0); }
}
(There are plenty of other options for wrapping trivially - the nice thing about Skip over Select/Where is that there's no delegate to execute pointlessly for each iteration.)
If you're not using .NET 3.5 you can write a very simple wrapper to do the same thing:
public static IEnumerable<T> Wrapper<T>(IEnumerable<T> source)
{
foreach (T element in source)
{
yield return element;
}
}

If you only need to iterate through the collection:
foreach (Foo f in bar.Foos)
then returning IEnumerable is enough.
If you need random access to items:
Foo f = bar.Foos[17];
then wrap it in ReadOnlyCollection.

If you do this then there's nothing stopping your callers casting the IEnumerable back to ICollection and then modifying it. ReadOnlyCollection removes this possibility, although it's still possible to access the underlying writable collection via reflection. If the collection is small then a safe and easy way to get around this problem is to return a copy instead.

I avoid using ReadOnlyCollection as much as possible, it is actually considerably slower than just using a normal List.
See this example:
List<int> intList = new List<int>();
//Use a ReadOnlyCollection around the List
System.Collections.ObjectModel.ReadOnlyCollection<int> mValue = new System.Collections.ObjectModel.ReadOnlyCollection<int>(intList);
for (int i = 0; i < 100000000; i++)
{
intList.Add(i);
}
long result = 0;
//Use normal foreach on the ReadOnlyCollection
TimeSpan lStart = new TimeSpan(System.DateTime.Now.Ticks);
foreach (int i in mValue)
result += i;
TimeSpan lEnd = new TimeSpan(System.DateTime.Now.Ticks);
MessageBox.Show("Speed(ms): " + (lEnd.TotalMilliseconds - lStart.TotalMilliseconds).ToString());
MessageBox.Show("Result: " + result.ToString());
//use <list>.ForEach
lStart = new TimeSpan(System.DateTime.Now.Ticks);
result = 0;
intList.ForEach(delegate(int i) { result += i; });
lEnd = new TimeSpan(System.DateTime.Now.Ticks);
MessageBox.Show("Speed(ms): " + (lEnd.TotalMilliseconds - lStart.TotalMilliseconds).ToString());
MessageBox.Show("Result: " + result.ToString());

Sometimes you may want to use an interface, perhaps because you want to mock the collection during unit testing. Please see my blog entry for adding your own interface to ReadonlyCollection by using an adapter.

Related

System.InvalidOperationException in my c# project

my project is about a class Account and 2 child classes (current account and deposit account).
in the main I created an arraylist of accounts
but I'm trying to delete an object in this method:
public static void Remove(ArrayList L, int accnb)
{
foreach(Account obj in L)
{
if(obj.AccN == accnb)
L.Remove(obj);
}
}
but I got an error : Collection was modified; enumeration operation may not execute.
all other methods like add or return string worked fine..
Don't remove elements while iterating over the collection with a foreach.
Also, I'd recommend using List<T> rather than ArrayList.
An easier way to solve the task at hand is to simply do:
public static void Remove(List<Account> L, int accnb) =>
L.RemoveAll(obj => obj.AccN == accnb);
foreach does not actually work with collections, but with Enumerators. While all collections are implicitly convertable into a Enumerator, Enumeartor rules still apply.
So basically your code is interpreted as:
temp IEnumerator = L.GetEnumerator();
foreach(Account obj in temp)
All enumerators have the rule that you can not change the underlying collection. Doing so will (has to) invalidate the enumerator. Wich will throw said except the next call of .Current().
As you can not change the collections while using a enumerator and foreach uses only Enumerators below the hood, that means you can not change the colelciton (inlcuding removing elements) while using foreach. You need one of the much more wordy loops to do this.
The items in an ArrayList are typed as object. Therefore, C# does not know that they have a member named AccN.
There is a strongly typed, generic equivalent of ArrayList named List<T>. Here you specify the type of the list items explicitly when you create the list with
List<Account> accounts = new List<Account>();
This list can also contain objects of the derived classes CurrentAccount and DepositAccount. Use it like this
public static void Remove(List<Account> L, int accnb)
{
foreach(Account acc in L)
{
if(acc.AccN == accnb)
L.Remove(acc);
}
}
Note: in C# 1.0 and C# 1.1 there were no generics. Therefore, the weakly typed collection ArrayList was implemented. Since generics were introduced in C# 2.0, this type is mostly obsolete.
With ArrayList you would have to cast the object to the right type to make it work
if(((Account)acc).AccN == accnb)
You also have another problem. You cannot change the very collection you are enumerating with foreach, because this confuses foreach. Use a for-loop instead and make sure you loop in reverse order to not change the indexes of the items ahead when removing items.
for (int i = L.Count - 1; i >= 0; i--) {
if (L[i].AccN == accnb) {
L.RemoveAt(i);
}
}
The C# Reference says:
The foreach statement is used to iterate through the collection to get the information that you want, but can not be used to add or remove items from the source collection to avoid unpredictable side effects. If you need to add or remove items from the source collection, use a for loop.

Are there good IList and IDictionary implementations in C# that support fail-safe iteration?

Per the title - are there good built-in options in C#/.NET for fail-safe iteration over an IList or an IDictionary?
Where I'm running into problems is with code similar to the following:
IList<Foo> someList = new List<Foo>();
//...
foreach (Foo foo in someList) {
if (foo.Bar) {
someList.remove(foo);
}
}
which throws the following after the first time foo.Bar is true:
Type: System.InvalidOperationException
Message: Collection was modified; enumeration operation may not execute.
I know the easy workaround is to do foreach (Foo foo in new List<Foo>(someList)), but it's annoying to have to remember to do that every. single. time. this comes up.
Coming from a Java background, that's something that can be handled neatly with a CopyOnWriteArrayList/ConcurrentHashMap (I'm aware that there are other penalties associated with using those lists.) Is there an equivalent in C# that I'm just not aware of?
If you're using .NET 4, there's ConcurrentDictionary<TKey, TValue> and ConcurrentBag<T> (and a queue and a stack in the same namespace). There's nothing which implements IList<T> as far as I'm aware though.
How about some fun with LINQ. Doesn't change behavior of List but makes writing your code nicer. Of course this only works on List not IList but still cool.
someList.RemoveAll(Foo => Foo.Bar == true);
A simple way to resolve your issue with modifying the collection would be to query the list for the items to remove and the iterate over the list of these items:
using System.Collections.Generic;
using System.Linq;
class Foo
{
public bool Bar { get; set; }
}
class Program
{
static void Main()
{
IList<Foo> someList = new List<Foo>() {
new Foo() { Bar = true },
new Foo() { Bar = false },
new Foo() { Bar = true }
};
var itemsToRemove = someList.Where(f => f.Bar == true).ToArray();
foreach (var foo in itemsToRemove)
{
someList.Remove(foo);
}
}
}
A list or dictionary that could handle any change while you are iterating over it, and figure out which items should be iterated or not, would be very complicated. It's better to solve that for each situation, as most situations are much simpler than the worst imaginable.
You can for example do like this:
someList.Where(foo => foo.Bar).ToList().ForEach(foo => someList.remove(foo));
In most cases that is more efficent than first copying the entire list, as the removed items usually are considerably fewer.
What you're looking for is called a robust iterator. The iterator pattern is implemented in .NET via IEnumerable<T> and IEnumerator<T>.
By default the iterators that you get with the Base Class Library are not robust. But, you can create your own. It will be a little tricky to implement because you have to make sure that you don't go over the same element twice, and you don't skip any elements. But it is certainly possible.
If you create a custom class that derives from List<T>, you can override the GetEnumerator() method to return the robust iterator and therefore use the foreach syntax.
In VB.net there is an class called Collection which implements a VB6-style collection. It's rather goofy in some ways (it's always a collection(Of String, Object)) but its enumerator can be used as you describe, and its performance is reasonably fast. It uses non-case-sensitive string comparisons, and I wish Microsoft had made a generic version, but its enumeration behavior might fit your needs and I think it should be possible to use it in C# if you import the right namespace.
If you only want to remove stuff use the mentioned RemoveAll-method. If you also want to do stuff with some of the elements you can do this:
for(int i = 0; i < list.Count; i++) {
var item = list[i];
if(item.Foo) {
list.RemoveAt(i--);
continue;
}
item.Bar();
}

Convert IList<T1> to IList<BaseT1>

As the easiest way to convert the IList<T1> to IList<BaseT1>?
IList<T1>.Count() is very large number!!!
class BaseT1 { };
class T1 : BaseT1
{
static public IList<BaseT1> convert(IList<T1> p)
{
IList<BaseT1> result = new List<BaseT1>();
foreach (BaseT1 baseT1 in p)
result.Add(baseT1);
return result;
}
}
You'll get much better performance in your implementation if you specify the size of the result list when it is initalized, and call the Add method on List<T> directly:
List<BaseT1> result = new List<BaseT1>(p.Count);
that way, it isn't resizing lots of arrays when new items get added. That should yield an order-of-magnitude speedup.
Alternatively, you could code a wrapper class that implements IList<BaseT1> and takes an IList<T1> in the constructor.
linq?
var baseList = derivedList.Cast<TBase>();
Edit:
Cast returns an IEnumerable, do you need it in a List? List can be an expensive class to deal with
IList<T1>.Count() is very large number!!!
Yes, which means that no matter what syntax sugar you use, the conversion is going to require O(n) time and O(n) storage. You cannot cast the list to avoid re-creating it. If that was possible, client code could add an element of BaseT1 to the list, violating the promise that list only contains objects that are compatible with T1.
The only way to get ahead is to return an interface type that cannot change the list. Which would be IEnumerable<BaseT1> in this case. Allowing you to iterate the list, nothing else. That conversion is automatic in .NET 4.0 thanks to its support for covariance. You'll have to write a little glue code in earlier versions:
public static IEnumerable<BaseT1> enumerate(IList<T1> p) {
foreach (BaseT1 item in p) yield return item;
}

Can't add/remove items from a collection while foreach is iterating over it

If I make my own implementation of IEnumerator interface, then I am able ( inside foreach statement )to add or remove items from a albumsList without generating an exception.But if foreach statement uses IEnumerator supplied by albumsList, then trying to add/delete ( inside the foreach )items from albumsList will result in exception:
class Program
{
static void Main(string[] args)
{
string[] rockAlbums = { "rock", "roll", "rain dogs" };
ArrayList albumsList = new ArrayList(rockAlbums);
AlbumsCollection ac = new AlbumsCollection(albumsList);
foreach (string item in ac)
{
Console.WriteLine(item);
albumsList.Remove(item); //works
}
foreach (string item in albumsList)
{
albumsList.Remove(item); //exception
}
}
class MyEnumerator : IEnumerator
{
ArrayList table;
int _current = -1;
public Object Current
{
get
{
return table[_current];
}
}
public bool MoveNext()
{
if (_current + 1 < table.Count)
{
_current++;
return true;
}
else
return false;
}
public void Reset()
{
_current = -1;
}
public MyEnumerator(ArrayList albums)
{
this.table = albums;
}
}
class AlbumsCollection : IEnumerable
{
public ArrayList albums;
public IEnumerator GetEnumerator()
{
return new MyEnumerator(this.albums);
}
public AlbumsCollection(ArrayList albums)
{
this.albums = albums;
}
}
}
a) I assume code that throws exception ( when using IEnumerator implementation A supplied by albumsList ) is located inside A?
b) If I want to be able to add/remove items from a collection ( while foreach is iterating over it), will I always need to provide my own implementation of IEnumerator interface, or can albumsList be set to allow adding/removing items?
thank you
Easiest way is to either reverse through the items like for(int i = items.Count-1; i >=0; i--), or loop once, gather all the items to remove in a list, then loop through the items to remove, removing them from the original list.
Generally it's discouraged to design collection classes that allow you to modify the collection while enumerating, unless your intention is to design something thread-safe specifically so that this is possible (e.g., adding from one thread while enumerating from another).
The reasons are myriad. Here's one.
Your MyEnumerator class works by incrementing an internal counter. Its Current property exposes the value at the given index in an ArrayList. What this means is that enumerating over the collection and removing "each" item will actually not work as expected (i.e., it won't remove every item in the list).
Consider this possibility:
The code you posted will actually do this:
You start by incrementing your index to 0, which gives you a Current of "rock." You remove "rock."
Now the collection has ["roll", "rain dogs"] and you increment your index to 1, making Current equal to "rain dogs" (NOT "roll"). Next, you remove "rain dogs."
Now the collection has ["roll"], and you increment your index to 2 (which is > Count); so your enumerator thinks it's finished.
There are other reasons this is a problematic implementation, though. For instance someone using your code might not understand how your enumerator works (nor should they -- the implementation should really not matter), and therefore not realize that the cost of calling Remove within a foreach block incurs the penalty of IndexOf -- i.e., a linear search -- on every iteration (see the MSDN documentation on ArrayList.Remove to verify this).
Basically, what I'm getting at is: you don't want to be able to remove items from within a foreach loop (again, unless you're designing something thread-safe... maybe).
OK, so what is the alternative? Here are a few points to get you started:
Don't design your collection to allow -- let alone expect -- modification within an enumeration. It leads to curious behavior such as the example I provided above.
Instead, if you want to provide bulk removal capabilities, consider methods such as Clear (to remove all items) or RemoveAll (to remove items matching a specified filter).
These bulk-removal methods can be implemented fairly easily. ArrayList already has a Clear method, as do most of the collection classes you might use in .NET. Otherwise, if your internal collection is indexed, a common method to remove multiple items is by enumerating from the top index using a for loop and calling RemoveAt on indices where removal is desired (notice this fixes two problems at once: by going backwards from the top, you ensure accessing each item in the collection; moreover, by using RemoveAt instead of Remove, you avoid the penalty of repeated linear searches).
As an added note, I would strongly encourage steering clear of non-generic collections such as ArrayList to begin with. Go with strongly typed, generic counterparts such as List(Of Album) instead (assuming you had an Album class -- otherwise, List(Of String) is still more typesafe than ArrayList).
Suppose I have a collection, an array for that matter
int[] a = { 1, 2, 3, 4, 5 };
I have a function
public IList<int> myiterator()
{
List<int> lst = new List<int>();
for (int i = 0; i <= 4; i++)
{
lst.Add(a[i]);
}
return lst;
}
Now i call this function and iterate over and try to add
var a = myiterator1();
foreach (var a1 in a)
{
a.Add(29);
}
Will cause a runtime exception
Here thing to notice is that if we are allowed to add for each element
in list
list will become something like {1,2,3,4,5,6}
then for every element and every newly added we keep on adding coz of that
we will be stuck in a infinite operation as it will again be repeated for every element
From the MSDN documentation for INotifyCollectionChanged:
You can enumerate over any collection
that implements the IEnumerable
interface. However, to set up dynamic
bindings so that insertions or
deletions in the collection update the
UI automatically, the collection must
implement the INotifyCollectionChanged
interface. This interface exposes the
CollectionChanged event that must be
raised whenever the underlying
collection changes.
WPF provides the
ObservableCollection<(Of <(T>)>)
class, which is a built-in
implementation of a data collection
that exposes the
INotifyCollectionChanged interface.
For an example, see How to: Create and
Bind to an ObservableCollection.
The individual data objects within the
collection must satisfy the
requirements described in the Binding
Sources Overview.
Before implementing your own
collection, consider using
ObservableCollection<(Of <(T>)>) or
one of the existing collection
classes, such as List<(Of <(T>)>),
Collection<(Of <(T>)>), and
BindingList<(Of <(T>)>), among many
others.
If you have an advanced scenario and
want to implement your own collection,
consider using IList, which provides a
non-generic collection of objects that
can be individually accessed by index
and provides the best performance.
Sounds to me that the problem is in the Collection itself, and not its Enumerator.

How can I add an item to a IEnumerable<T> collection?

My question as title above. For example
IEnumerable<T> items = new T[]{new T("msg")};
items.ToList().Add(new T("msg2"));
but after all it only has 1 item inside. Can we have a method like items.Add(item) like the List<T>?
You cannot, because IEnumerable<T> does not necessarily represent a collection to which items can be added. In fact, it does not necessarily represent a collection at all! For example:
IEnumerable<string> ReadLines()
{
string s;
do
{
s = Console.ReadLine();
yield return s;
} while (!string.IsNullOrEmpty(s));
}
IEnumerable<string> lines = ReadLines();
lines.Add("foo") // so what is this supposed to do??
What you can do, however, is create a new IEnumerable object (of unspecified type), which, when enumerated, will provide all items of the old one, plus some of your own. You use Enumerable.Concat for that:
items = items.Concat(new[] { "foo" });
This will not change the array object (you cannot insert items into to arrays, anyway). But it will create a new object that will list all items in the array, and then "Foo". Furthermore, that new object will keep track of changes in the array (i.e. whenever you enumerate it, you'll see the current values of items).
The type IEnumerable<T> does not support such operations. The purpose of the IEnumerable<T> interface is to allow a consumer to view the contents of a collection. Not to modify the values.
When you do operations like .ToList().Add() you are creating a new List<T> and adding a value to that list. It has no connection to the original list.
What you can do is use the Add extension method to create a new IEnumerable<T> with the added value.
items = items.Add("msg2");
Even in this case it won't modify the original IEnumerable<T> object. This can be verified by holding a reference to it. For example
var items = new string[]{"foo"};
var temp = items;
items = items.Add("bar");
After this set of operations the variable temp will still only reference an enumerable with a single element "foo" in the set of values while items will reference a different enumerable with values "foo" and "bar".
EDIT
I contstantly forget that Add is not a typical extension method on IEnumerable<T> because it's one of the first ones that I end up defining. Here it is
public static IEnumerable<T> Add<T>(this IEnumerable<T> e, T value) {
foreach ( var cur in e) {
yield return cur;
}
yield return value;
}
Have you considered using ICollection<T> or IList<T> interfaces instead, they exist for the very reason that you want to have an Add method on an IEnumerable<T>.
IEnumerable<T> is used to 'mark' a type as being...well, enumerable or just a sequence of items without necessarily making any guarantees of whether the real underlying object supports adding/removing of items. Also remember that these interfaces implement IEnumerable<T> so you get all the extensions methods that you get with IEnumerable<T> as well.
In .net Core, there is a method Enumerable.Append that does exactly that.
The source code of the method is available on GitHub..... The implementation (more sophisticated than the suggestions in other answers) is worth a look :).
A couple short, sweet extension methods on IEnumerable and IEnumerable<T> do it for me:
public static IEnumerable Append(this IEnumerable first, params object[] second)
{
return first.OfType<object>().Concat(second);
}
public static IEnumerable<T> Append<T>(this IEnumerable<T> first, params T[] second)
{
return first.Concat(second);
}
public static IEnumerable Prepend(this IEnumerable first, params object[] second)
{
return second.Concat(first.OfType<object>());
}
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> first, params T[] second)
{
return second.Concat(first);
}
Elegant (well, except for the non-generic versions). Too bad these methods are not in the BCL.
No, the IEnumerable doesn't support adding items to it. The alternative solution is
var myList = new List(items);
myList.Add(otherItem);
To add second message you need to -
IEnumerable<T> items = new T[]{new T("msg")};
items = items.Concat(new[] {new T("msg2")})
I just come here to say that, aside from Enumerable.Concat extension method, there seems to be another method named Enumerable.Append in .NET Core 1.1.1. The latter allows you to concatenate a single item to an existing sequence. So Aamol's answer can also be written as
IEnumerable<T> items = new T[]{new T("msg")};
items = items.Append(new T("msg2"));
Still, please note that this function will not change the input sequence, it just return a wrapper that put the given sequence and the appended item together.
Not only can you not add items like you state, but if you add an item to a List<T> (or pretty much any other non-read only collection) that you have an existing enumerator for, the enumerator is invalidated (throws InvalidOperationException from then on).
If you are aggregating results from some type of data query, you can use the Concat extension method:
Edit: I originally used the Union extension in the example, which is not really correct. My application uses it extensively to make sure overlapping queries don't duplicate results.
IEnumerable<T> itemsA = ...;
IEnumerable<T> itemsB = ...;
IEnumerable<T> itemsC = ...;
return itemsA.Concat(itemsB).Concat(itemsC);
Others have already given great explanations regarding why you can not (and should not!) be able to add items to an IEnumerable. I will only add that if you are looking to continue coding to an interface that represents a collection and want an add method, you should code to ICollection or IList. As an added bonanza, these interfaces implement IEnumerable.
you can do this.
//Create IEnumerable
IEnumerable<T> items = new T[]{new T("msg")};
//Convert to list.
List<T> list = items.ToList();
//Add new item to list.
list.add(new T("msg2"));
//Cast list to IEnumerable
items = (IEnumerable<T>)items;
Easyest way to do that is simply
IEnumerable<T> items = new T[]{new T("msg")};
List<string> itemsList = new List<string>();
itemsList.AddRange(items.Select(y => y.ToString()));
itemsList.Add("msg2");
Then you can return list as IEnumerable also because it implements IEnumerable interface
Instances implementing IEnumerable and IEnumerator (returned from IEnumerable) don't have any APIs that allow altering collection, the interface give read-only APIs.
The 2 ways to actually alter the collection:
If the instance happens to be some collection with write API (e.g. List) you can try casting to this type:
IList<string> list = enumerableInstance as IList<string>;
Create a list from IEnumerable (e.g. via LINQ extension method toList():
var list = enumerableInstance.toList();
IEnumerable items = Enumerable.Empty(T);
List somevalues = new List();
items.ToList().Add(someValues);
items.ToList().AddRange(someValues);
Sorry for reviving really old question but as it is listed among first google search results I assume that some people keep landing here.
Among a lot of answers, some of them really valuable and well explained, I would like to add a different point of vue as, to me, the problem has not be well identified.
You are declaring a variable which stores data, you need it to be able to change by adding items to it ? So you shouldn't use declare it as IEnumerable.
As proposed by #NightOwl888
For this example, just declare IList instead of IEnumerable: IList items = new T[]{new T("msg")}; items.Add(new T("msg2"));
Trying to bypass the declared interface limitations only shows that you made the wrong choice.
Beyond this, all methods that are proposed to implement things that already exists in other implementations should be deconsidered.
Classes and interfaces that let you add items already exists. Why always recreate things that are already done elsewhere ?
This kind of consideration is a goal of abstracting variables capabilities within interfaces.
TL;DR : IMO these are cleanest ways to do what you need :
// 1st choice : Changing declaration
IList<T> variable = new T[] { };
variable.Add(new T());
// 2nd choice : Changing instantiation, letting the framework taking care of declaration
var variable = new List<T> { };
variable.Add(new T());
When you'll need to use variable as an IEnumerable, you'll be able to. When you'll need to use it as an array, you'll be able to call 'ToArray()', it really always should be that simple. No extension method needed, casts only when really needed, ability to use LinQ on your variable, etc ...
Stop doing weird and/or complex things because you only made a mistake when declaring/instantiating.
Maybe I'm too late but I hope it helps anyone in the future.
You can use the insert function to add an item at a specific index.
list.insert(0, item);
Sure, you can (I am leaving your T-business aside):
public IEnumerable<string> tryAdd(IEnumerable<string> items)
{
List<string> list = items.ToList();
string obj = "";
list.Add(obj);
return list.Select(i => i);
}

Categories