Producing an abstract collection from an abstract collection - c#

This issue has been bugging me for a while. Abstractly speaking, regardless of language, there are often situations when you want to have a method like this:
Collection method(Collection c) {
// select some elements from c based on some filter
// and return a new collection
}
Now, Collection is in this case some abstract class (Like say IList in C# or List in Java) with several implementations. I've been wondering what exactly is the right procedure to produce the abstract collection?
Is it ok to create a concrete collection inside the method and return it? Like:
Collection method(Collection c) {
Collection cc = new ConcreteCollection();
// select some elements from c based on some filter
return cc;
}
This of course puts a constraint on the resulting collection and will produce problems in case, for some reason, we want to cast the result of the method to a different concrete collection than the one used inside the method.
Or, use reflection to determine the actual concrete type of c and create an instance of that class:
Collection method(Collection c) {
Collection cc = c.getClass().newInstance();
// select some elements from c based on some filter
return cc;
}
For some reason this does not seem very "elegant" to me. I would greatly appreciate some insight in this matter.

(Speaking for java). The reason you're returning Collection (an interface) rather than a concrete type (such as ArrayList) is that you're telling the user that they shouldn't care about what the actual concrete type being used is. This leaves you free to choose the appropriate type for your library/api.
If you're enforcing a particular concrete class, then you should be returning that concrete class, rather than the interface.
So, they shouldn't be casting your return type to anything else other than Collection. See
When should I return the Interface and when the concrete class?.

In Java, there are actually some good examples of how to do this in the java.util.Collections class. Instead of taking a Collection and returning a Collection, the key methods take two collections, the "src" and the "dest". For example, Look at the signature of the copy method:
public static <T> void copy(List<? super T> dest, List<? extends T> src)
This puts the responsibility of instantiating the destination list on the caller.
I think you could do the same thing when you want to create a method that acts on a src Collection and puts the results into a destination Collection (rather than Lists).
I agree with Matthew Farwell's answer that you probably just want to return the interface and utilize that, but for the times when you really do need to work with a specific implementing class you can do it the same way the Collections class does it.

One approach you could take is to create a Collection implementation that delegates calls through to the original Collection. This defers the potentially expensive operation of filtering a large Collection until you need to explicitly read elements. It also saves memory.
Example
public interface Filter<T> {
boolean include(T t);
}
public class FilterCollection<T> implements Collection<T> {
private final Collection<T> orig;
private final Filter<T> filter;
public FilterCollection(Collection<T> orig, Filter<T> filter) {
this.orig = orig;
this.filter = filter;
}
public int size() {
int sz = 0;
for (T t : orig) {
if (filter.include(t)) {
++sz;
}
}
return sz;
}
public boolean contains(Object o) {
return o instanceof T && filter.include((T) o) && orig.contains(o);
}
public boolean add(T t) {
if (!filter.include(t)) {
throw new IllegalArgumentException("Element lies outside filter bounds.");
}
orig.add(t);
}
}

The caller should assume a given type of Collection is returned.
Instead it should either copy to the desired type or pass the desired type.
e.g.
Set<T> set2 = new HashSet<T>(filter(set));
List<T> list2 = new ArrayList<T>(filter(list));
or
filter(set2, set); // the target collection is passed.
filter(list2, list);

To the question about ConcreteCollection, it is definitely allowable.
To the concern about having a different concrete collection expected, there are a few ways to go around the problem:
Change the return type of the method. Example:
ConcreteCollection method(Collection c){
ConcreteCollection cc=new ConcreteCollection
for(Object x: c){
//do something
}
return cc
}
Make use of polymorphism. Example:
Collection x=method(c)
x.add(new Object) //add is a method defined within the abstract Collection
Use some utilities to cast the type. Example:
LinkedList h=Collections.toLinkedList(method(c))
Hoped my answer helped. ^^

As far as I can understand, you want to know how to make a method that accepts generic list and returns another modified generic list.
So, my advice will be to use an abstract type that implements method to modify its state.
IList<object> list = new List<object>();
list.Add(new object());
list.Remove(obj);
Or as showed above, instantiate a list that implements IList (or the Java equivalent) work with this instance and return the result as a IList
Edit
If you want to filter some item from a list to a new one, generics can help (I don't know if this feature exists in Java).
public IList<T> Filter<T>(IList<T> list)
{
var result = new List<T>();
result.Add(list[0]); // Or whatever filtering method
return result;
}

If you want your method to accept as many different collection types as possible, and you want to be sure that the result is the same implementation type as what you put in, you might want to use a void method which directly modifies the supplied collection. For instance:
import com.google.common.base.Predicate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class Testy {
private static <T> void filter(Iterable<T> collection, Predicate<T> filter) {
Iterator<T> iterator = collection.iterator();
while (iterator.hasNext()) {
if (!filter.apply(iterator.next())) { // Condition goes here
iterator.remove();
}
}
}
public static void main(String... args) {
List<String> list = new ArrayList<String>();
list.addAll(Arrays.asList("A", "B", "C", "D"));
filter(list, new Predicate<String>() { // Anonymous filter (predicate)
#Override public boolean apply(String input) {
return input.equals("B");
}
});
System.out.println(list); // Prints ["B"]
}
}
The helper method filter takes an Iterable, the simplest type required for iterating over something. Apply the filter to each element, and if the predicate (filter) returns false, remove that element from the underlying collection with Iterator.remove().
The Predicate<T> interface here comes from Google. You can easily write your own if you don't wish to import it. The only required method is apply(T) which returns a boolean. Either that, or just write your condition directly inside the loop and get rid of the second parameter.
This method is the most efficient if your original collection is mutable and you don't wish to keep any intermediate results.
Another option is to use Google Collections Collections2.filter(Collection<E>, Predicate<E>) which returns a Collection<E> just like in your question. Similarly, the Iterables class will do the same thing, but create lazy iterables where the filters are only applied when actually doing the iterating.

Related

What is the default concrete type of IEnumerable

(Sorry for the vague title; couldn't think of anything better. Feel free to rephrase.)
So let's say my function or property returns an IEnumerable<T>:
public IEnumerable<Person> Adults
{
get
{
return _Members.Where(i => i.Age >= 18);
}
}
If I run a foreach on this property without actually materializing the returned enumerable:
foreach(var Adult in Adults)
{
//...
}
Is there a rule that governs whether IEnumerable<Person> will be materialized to array or list or something else?
Also is it safe to cast Adults to List<Person> or Array without calling ToList() or ToArray()?
Edit
Many people have spent a lot of effort into answering this question. Thanks to all of them. However, the gist of this question still remains unanswered. Let me put in some more details:
I understand that foreach doesn't require the target object to be an array or list. It doesn't even need to be a collection of any kind. All it needs the target object to do is to implement enumeration. However if I place inspect the value of target object, it reveals that the actual underlying object is List<T> (just like it shows object (string) when you inspect a boxed string object). This is where the confusion starts. Who performed this materialization? I inspected the underlying layers (Where() function's source) and it doesn't look like those functions are doing this.
So my problem lies at two levels.
First one is purely theoretical. Unlike many other disciplines like physics and biology, in computer sciences we always know precisely how something works (answering #zzxyz's last comment); so I was trying to dig about the agent who created List<T> and how it decided it should choose a List and not an Array and if there is a way of influencing that decision from our code.
My second reason was practical. Can I rely on the type of actual underlying object and cast it to List<T>? I need to use some List<T> functionality and I was wondering if for example ((List<Person>)Adults).BinarySearch() is as safe as Adults.ToList().BinarySearch()?
I also understand that it isn't going to create any performance penalty even if I do call ToList() explicitly. I was just trying to understand how it is working. Anyway, thanks again for the time; I guess I have spent just too much time on it.
In general terms all you need for a foreach to work is to have an object with an accessible GetEnumerator() method that returns an object that has the following methods:
void Reset()
bool MoveNext()
T Current { get; private set; } // where `T` is some type.
You don't even need an IEnumerable or IEnumerable<T>.
This code works as the compiler figures out everything it needs:
void Main()
{
foreach (var adult in new Adults())
{
Console.WriteLine(adult.ToString());
}
}
public class Adult
{
public override string ToString() => "Adult!";
}
public class Adults
{
public class Enumerator
{
public Adult Current { get; private set; }
public bool MoveNext()
{
if (this.Current == null)
{
this.Current = new Adult();
return true;
}
this.Current = null;
return false;
}
public void Reset() { this.Current = null; }
}
public Enumerator GetEnumerator() { return new Enumerator(); }
}
Having a proper enumerable makes the process work more easily and more robustly. The more idiomatic version of the above code is:
public class Adults
{
private class Enumerator : IEnumerator<Adult>
{
public Adult Current { get; private set; }
object IEnumerator.Current => this.Current;
public void Dispose() { }
public bool MoveNext()
{
if (this.Current == null)
{
this.Current = new Adult();
return true;
}
this.Current = null;
return false;
}
public void Reset()
{
this.Current = null;
}
}
public IEnumerator<Adult> GetEnumerator()
{
return new Enumerator();
}
}
This enables the Enumerator to be a private class, i.e. private class Enumerator. The interface then does all of the hard work - it's not even possible to get a reference to the Enumerator class outside of Adults.
The point is that you do not know at compile-time what the concrete type of the class is - and if you did you may not even be able to cast to it.
The interface is all you need, and even that isn't strictly true if you consider my first example.
If you want a List<Adult> or an Adult[] you must call .ToList() or .ToArray() respectively.
There is no such thing as a default concrete type for any interface.
The entire point of an interface is to guarantee properties, methods, events or indexers, without the user need of any knowledge of the concrete type that implements it.
When using an interface, all you can know is the properties, methods, events and indexers this interface declares, and that's all you actually need to know. That's just another aspect of encapsulation - same as when you are using a method of a class you don't need to know the internal implementation of that method.
To answer your question in the comments:
who decides that concrete type in case we don't, just as I did above?
That's the code that created the instance that's implementing the interface.
Since you can't do var Adults = new IEnumerable<Person> - it has to be a concrete type of some sort.
As far as I see in the source code for linq's Enumerable extensions - the where returns either an instance of Iterator<TSource> or an instance of WhereEnumerableIterator<TSource>. I didn't bother checking further what exactly are those types, but I can pretty much guarantee they both implement IEnumerable, or the guys at Microsoft are using a different c# compiler then the rest of us... :-)
The following code hopefully highlights why neither you nor the compiler can assume an underlying collection:
public class OneThroughTen : IEnumerable<int>
{
private static int bar = 0;
public IEnumerator<int> GetEnumerator()
{
while (true)
{
yield return ++bar;
if (bar == 10)
{ yield break; }
}
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
class Program
{
static void Main(string[] args)
{
IEnumerable<int> x = new OneThroughTen();
foreach (int i in x)
{ Console.Write("{0} ", i); }
}
}
Output being, of course:
1 2 3 4 5 6 7 8 9 10
Note, the code above behaves extremely poorly in the debugger. I don't know why. This code behaves just fine:
public IEnumerator<int> GetEnumerator()
{
while (bar < 10)
{
yield return ++bar;
}
bar = 0;
}
(I used static for bar to highlight that not only does the OneThroughTen not have a specific collection, it doesn't have any collection, and in fact has no instance data whatsoever. We could just as easily return 10 random numbers, which would've been a better example, now that I think on it :))
From your edited question and comments it sounds like you understand the general concept of using IEnumerable, and that you cannot assume that "a list object backs all IEnumerable objects". Your real question is about something that has confused you in the debugger, but we've not really been able to understand exactly what it is you are seeing. Perhaps a screenshot would help?
Here I have 5 IEnumerable<int> variables which I assign in various ways, along with how the "Watch" window describes them. Does this show the confusion you are having? If not, can you construct a similarly short program and screenshot that does?
Coming a bit late into the party here :)
Actually Linq's "Where" decides what's going to be the underlying implementation of IEnumerable's GetEnumerator.
Look at the source code:
https://github.com/dotnet/runtime/blob/918e6a9a278bc66fb191c43d4db4a71e63ffad31/src/libraries/System.Linq/src/System/Linq/Where.cs#L59
You'll see that based on the "source" type, the methods return "WhereSelectArrayIterator" or "WhereSelectListIterator" or a more generic "WhereSelectEnumerableSelector".
Each of this objects implement the GetEnumerator over an Array, or a List, so I'm pretty sure that's why you see the underlying object type being one of these on VS inspector.
Hope this helps clarifying.
I have been digging into this myself. I believe the 'underlying type' is an iterator method, not an actual data structure type.
An iterator method defines how to generate the objects in a sequence
when requested.
https://learn.microsoft.com/en-us/dotnet/csharp/iterators#enumeration-sources-with-iterator-methods
In my usecase/testing, the iterator is System.Linq.Enumerable.SelectManySingleSelectorIterator. I don't think this is a collection data type. It is a method that can enumerate IEnumerables.
Here is a snippet:
public IEnumerable<Item> ItemsToBuy { get; set; }
...
ItemsToBuy = Enumerable.Range(1, rng.Next(1, 20))
.Select(RandomItem(rng, market))
.SelectMany(e => e);
The property is IEnumerable and .SelectMany returns IEnumerable. So what is the actual collection data structure? I don't think there is one in how I am interpreting 'collection data structure'.
Also is it safe to cast Adults to List or Array without
calling ToList() or ToArray()?
Not for me. When attempting to cast ItemsToBuy collection in a foreach loop I get the following runtime exception:
{"Unable to cast object of type
'SelectManySingleSelectorIterator2[System.Collections.Generic.IEnumerable1[CashMart.Models.Item],CashMart.Models.Item]'
to type 'CashMart.Models.Item[]'."}
So I could not cast, but I could .ToArray(). I do suspect there is a performance hit as I would think that the IEnumerable would have to 'do things' to make it an array, including memory allocation for the array even if the entities are already in memory.
However if I place inspect the value of target object, it reveals that
the actual underlying object is List
This was not my experience and I think it may depend on the IEnumerable source as well as the LinQ provider. If I add a where, the returned iterator is:
System.Linq.Enumerable.WhereEnumerableIterator
I am unsure what your _Member source is, but using LinQ-to-Objects, I get an iterator. LinQ-to-Entities must call the database and store the result set in memory somehow and then enumerate on that result. I would doubt that it internally makes it a List, but I don't know much. I suspect instead that _Members may be a List somewhere else in your code thus, even after the .Where, it shows as a List.

Internal class masked by object

Assume that class (B)'s public function has the return line:
return (object)(new List<A>{Some elements})
where A is an internal and sealed class. I cannot change the code of A or B.
After I call this function in B, how do I find the first element of that list. C# does not let me cast that list back into List<A> because A is internal.
Just because you can read the source code or disassemble the code, you should not rely on the current implementation, rather try to use the public interface.
List<A> implements the non-generic IList, so you can cast back to IEnumerable or IList if you really look for trouble.
You can cast a generic List to the non-generic IEnumerable, iterate over that, and then use Object.ToString() to get information about the B instances, or you can just return the reference.
Object obj = new List<string> () { "dd", "ee" };
IEnumerable enumerable = obj as IEnumerable;
bool foundSomething = false;
foreach (var thing in enumerable)
{
if(!foundSomething)
{
// Console.Write(thing.ToString()); If you want
foundSomething = true;
return thing;
}
}
Perhaps I'm misunderstanding the question here, but if A is sealed, you can still write an extension method to iterate or handle the list.
Extension methods for sealed class in c#
You can use interface covariance to cast to IEnumerable<object> and then use some of LINQ's extension methods:
var aItems = (IEnumerable<object>) B.Foo();
Console.WriteLine(aItems.First());
To get first element without touching anything you can do this:
object result = b.MethodThatReturnsList();
object firstEl = ((IList)result)[0];
Problem is that firstElvariable can only be object and you can't cast it to A because it is not accessible. Not very helpful though.
Here is the real problem: you can't declare public methods that return some private/internal types. You will get this compilation error.
Solution is to design a public interface that A will implement and return List<IYourInterface>. Another option is to have public base class.

How to set a generic constraint that the type is a List?

private static void PrintEachItemInList<T>(T anyList)
Where T:System.Collections.Generic.List<T>
{
foreach (var t in T)
{
//Do whatever
}
}
In the above code (which is wrong) all I want to do it to set a constraint that T is a List.
The aim is not to get this example to work, the aim is to understand how can I set a constraint that the type is a list? I am an amateur in generics and am trying to figure things out :(
Maybe you want two type parameters, as in:
private static void PrintEachItemInList<TList, TItem>(TList anyType)
where TList : System.Collections.Generic.List<TItem>
This is useful if you use classes that actually derive from List<>. If you want anything that acts as a list, consider constraining to the interface IList<> instead. It will then work for List<>, single-dimensional arrays, and custom classes implementing the interface (but not necessarily deriving from List<>).
Edit: As pointed out by the comment, this method is cumbersome to use because the compiler will not infer the two type arguments, so they will have to be given explicitly when calling the method.
Consider just using:
private static void PrintEachItemInList<TItem>(List<TItem> anyType)
Because anything which derives from a List<> is assignable to List<>, the method can be called with derived classes as arguments, and in many cases the type TItem can be inferred automatically by the compiler.
Still consider using the interface IList<>.
If all you want to do, is read from the list, use IReadOnlyList<TItem> instead of IList<TItem>. This signals to the caller that you won't change his list. Still no cast syntax is required when calling, for example: PrintEachItemInList(new[] { 2, 3, 5, 7, });. The type IReadOnlyList<> is new in .NET version 4.5.
If all you want to do is read from the list, and you don't want to use the indexer (no anyType[idx]), and you don't want to use the .Count property, and in fact, all you want to do is foreach through the list, use IEnumerable<TItem>. Again, you signal that you won't change people's lists.
Both IReadOnlyList<> and IEnumerable<> are covariant in their generic argument (type parameter).
Declare your input parameter as IList<T>. If you want to make your input sequence as abstract as possible - use IEnumerable<T> instead of IList<T>
private static void PrintEachItemInList<T>(IList<T> sequence)
{
foreach (T element in sequence)
{
//Do whatever
}
}
Your function should simply accept a list, and you don't need a constraint:
private static void PrintEachItemInList<T>(IList<T> list)
{
foreach (var t in list)
{
//Do whatever
}
}
However, if you only want to iterate the list you can make your code more general by changing the parameter type to one of these base interfaces:
IEnumerable<T> - allows foreach
IReadOnlyCollection - same as above and provides the count of elements in the collection
IReadOnlyList - same as above and allows element access by index
ICollection<T> - an IEnumerable<T> that provides an element count and methods to add and remove elements
IList<T> - same as above and allows you to add and remove elements by index
if you want to use parametric polymorphism to express that constraint, in c# you need two type parameters (since you don't get higher-order types):
private static void PrintEachItemInList<X, T>(X anyType) where X:System.Collections.Generic.List<T>
{
foreach (var t in anyType)
{
Console.WriteLine(t.ToString());
}
}
but then you need to call like this:
PrintEachItemInList<List<string>,string>(new List<string>() {"a", "b"});
You would typically write this as:
private static void PrintEachItemInList<T>(List<T> anyType)
{
// do work
}
However, in this case, I would recommend using IEnumerable<T> instead:
private static void PrintEachItemInList<T>(IEnumerable<T> anyType)
{
// do work
}
This will still allow you to use foreach but allow your method to be more flexible, as it will work work List<T> but also any other collection which implements IEnumerable<T>. If you must have list semantics within the method, you could use IList<T> , but your sample code (and method name) suggests IEnumerable<T> would be more appropriate.

Chaining Extension methods in C#

Is it possible to create an extension method that returns the instance that is invoking the extension method?
I would like to have an extension method for anything that inherits from ICollection<T>, returns the object. Much like how jQuery always returns the jquery object.
public static object AddItem<T>(this ICollection<T> collection, T itemToAdd)
{
collection.Add(itemToAdd);
return collection;
{
I imagine something like above, but I am not sure how to get back to the parent to the "this" object type for use of something like this:
List<int> myInts = new List<int>().AddItem(5);
EDIT: Just wanted to be clear that i was hoping for a single generic constraint solution.
If you need to return the specific type, you can use a generic constraint:
public static TCollection AddItem<TCollection, TElement>(
this TCollection collection,
TElement itemToAdd)
where TCollection : ICollection<TElement>
{
collection.Add(itemToAdd);
return collection;
}
I tested this and it works in VS2010.
Update (regarding jQuery):
jQuery chaining works very well because JavaScript uses dynamic typing. C# 4.0 supports dynamic, so you can do this:
public static dynamic AddItem<T>(this ICollection<T> collection, T itemToAdd)
{
collection.Add(itemToAdd);
return collection;
}
However, I do recommend the generic constraint version, since it is more type-safe, more efficient, and allows IntelliSense on the returned type. In more complex scenarios, generic constraints aren't always capable of expressing what you need; in those cases, dynamic can be used (though it won't bind to additional extension methods, so it doesn't work well with chaining).
While I don't have VS open to try this, something along these lines should work:
public static TCollection AddItem<TCollection, TItem>(TCollection collection,
TItem itemToAdd)
where TCollection : ICollection<TItem>
{
collection.Add(itemToAdd);
return collection;
}
You seem to have 2 conflicting goals, and it comes down to what do you want your extension method to return:
The instance that invoked the extension method (the collection)
OR the item that was added to the collection
From your example usage, quoted here:
List<int> myInts = new List<int>().AddItem(5);
You make it look like you want to return the collection. In any case, that assignment still won't work without a cast, since your extension method would need to have a return type of ICollection, like this:
public static ICollection<T> AddItem<T>(this ICollection<T> collection, T itemToAdd)
{
collection.Add(itemToAdd);
return collection;
}
That would allow you to do this:
List<int> myList = (List<int>) new List<int>().AddItem(5);
Now if you'd rather return the object that was added, you still shouldn't have a return type of object. You should take advantage of your generic type parameter, and return T, like this:
public static T AddItem<T>(this ICollection<T> collection, T itemToAdd)
{
collection.Add(itemToAdd);
return itemToAdd;
}
However, if you're returning the item that was added, you won't be able to chain like this:
List<int> myList = (List<int>) new List<int>().AddItem(5);
, since the return type of AddItem(5) is not ICollection, but it's T (int, in this case). You can still chain though, just off of the value added, like this:
List<int> myList = new List<int>();
myList.AddItem(5).DoSomethingWithMyInt(); // Not very useful in this case
It seems like the first scenario is more useful (returning the collection), because it does allow you chain, right off of the initial assignment statement. Here's a larger example of that:
List<int> myList = (List<int>) new List<int>().AddItem(1).AddItem(2);
Or, if you don't want to cast, you can call ToList() on the ICollection that comes back:
List<int> myList = new List<int>().AddItem(1).AddItem(2).ToList();
EDIT: Just wanted to be clear that i was hoping for a single generic constraint solution.
In this case you're out of luck because return type conversions can be covariant, but not contravariant (i.e. you cannot implicitly convert from ICollection<T> to List<T>), so without a generic return type this cannot be done.
What's wrong with specifying 2 type parameters anyway? They can be inferred by the arguments you provide to the function so you won't even really notice them in your calling code.
Just return ICollection<T>instead of object and everything should work like you intended it.

How to allow iteration over a private collection but not modification?

If I have the following class member:
private List<object> obs;
and I want to allow traversal of this list as part of the class' interface, how would I do it?
Making it public won't work because I don't want to allow the list to be modified directly.
You would expose it as an IEnumerable<T>, but not just returning it directly:
public IEnumerable<object> Objects { get { return obs.Select(o => o); } }
Since you indicated you only wanted traversal of the list, this is all you need.
One might be tempted to return the List<object> directly as an IEnumerable<T>, but that would be incorrect, because one could easily inspect the IEnumerable<T> at runtime, determine it is a List<T> and cast it to such and mutate the contents.
However, by using return obs.Select(o => o); you end up returning an iterator over the List<object>, not a direct reference to the List<object> itself.
Some might think that this qualifies as a "degenerate expression" according to section 7.15.2.5 of the C# Language Specification. However, Eric Lippert goes into detail as to why this projection isn't optimized away.
Also, people are suggesting that one use the AsEnumerable extension method. This is incorrect, as the reference identity of the original list is maintained. From the Remarks section of the documentation:
The AsEnumerable<TSource>(IEnumerable<TSource>) method has no effect other than to change the compile-time type of source from a type that implements IEnumerable<T> to IEnumerable<T> itself.
In other words, all it does is cast the source parameter to IEnumerable<T>, which doesn't help protect referencial integrity, the original reference is returned and can be cast back to List<T> and be used to mutate the list.
You can use a ReadOnlyCollection or make a copy of the List and return it instead (considering the performance penalty of the copy operation). You can also use List<T>.AsReadOnly.
This has already been said, but I don't see any of the answers as being superclear.
The easiest way is to simply return a ReadOnlyCollection
private List<object> objs;
public ReadOnlyCollection<object> Objs {
get {
return objs.AsReadOnly();
}
}
The drawback with this is, that if you want to change your implementation later on, then some callers may already be dependent on the fact, that the collection provides random access. So a safer definition would be to just expose an IEnumerable
public IEnumerable<object> Objs {
get {
return objs.AsReadOnly();
}
}
Note that you don't have to call AsReadOnly() to compile this code. But if you don't, the caller my just cast the return value back to a List and modify your list.
// Bad caller code
var objs = YourClass.Objs;
var list = objs as List<object>;
list.Add(new object); // They have just modified your list.
The same is potential problem also exists with this solution
public IEnumerable<object> Objs {
get {
return objs.AsEnumerable();
}
}
So I would definately recommend that you call AsReadOnly() on you list, and return that value.
To your Interface add the following method signature:
public IEnumerable TraverseTheList()
Implimented as so:
public IEnumerable<object> TraverseTheList()
{
foreach( object item in obj)
{
yield return item;
}
}
that will allow you to do the following:
foreach(object item in Something.TraverseTheList())
{
// do something to the item
}
The yield return tells the compiler to build an enumerator for you.
You can do this in two ways:
Either By converting the list into a Readonly collection:
new System.Collections.ObjectModel.ReadOnlyCollection<object>(this.obs)
Or by returning an IEnumerable of the items:
this.obs.AsEnumerable()
Expose a ReadOnlyCollection<T>
Interesting post and dialog on this very issue: http://davybrion.com/blog/2009/10/stop-exposing-collections-already/.
Have you considered deriving a class from System.Collections.ReadOnlyCollectionBase?
Just return an IReadOnlyCollection.
private List<object> obs;
IReadOnlyCollection<object> GetObjects()
{
return obs;
}

Categories