Collections, plus collection of base type - concept
-
Good point Luc, thanks. I will look into this further.
Luc Pattyn wrote:
FWIW: are you familiar with AsReadOnly?
Yes I am. The collections need to have internal constructors and an internal Add method so it was just as easy to create a simple base class (not the MyBase referred to in the OP) with this functionality that the other collections derive from.
Dave
Binging is like googling, it just feels dirtier. Please take your VB.NET out of our nice case sensitive forum. Astonish us. Be exceptional. (Pete O'Hanlon)
BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)Hi Davey, If you did not use IList(s) (as Luc suggested) set to some Collection modified by the 'ReadOnly() method, as in:
public static IList<MyFoo> MyFoos;
//
// and later ... after creating and initializing 'MyFooCollection:
//
MyFoos = MyFooCollection.AsReadOnly();Then I am very curious what technique(s) you did use to make the Collections in your code ReadOnly. thanks, Davey (and Luc)
When I consider the brief span of my life, swallowed up in the eternity before and after, the little space which I fill, and even can see, engulfed in the infinite immensity of spaces of which I am ignorant, and which knows me not, I am frightened, and am astonished at being here rather than there; for there is no reason why here rather than there, now rather than then.
Blaise Pascal
-
Hi Davey, If you did not use IList(s) (as Luc suggested) set to some Collection modified by the 'ReadOnly() method, as in:
public static IList<MyFoo> MyFoos;
//
// and later ... after creating and initializing 'MyFooCollection:
//
MyFoos = MyFooCollection.AsReadOnly();Then I am very curious what technique(s) you did use to make the Collections in your code ReadOnly. thanks, Davey (and Luc)
When I consider the brief span of my life, swallowed up in the eternity before and after, the little space which I fill, and even can see, engulfed in the infinite immensity of spaces of which I am ignorant, and which knows me not, I am frightened, and am astonished at being here rather than there; for there is no reason why here rather than there, now rather than then.
Blaise Pascal
Well a simple collection (generic) that is immutable (so naturally read only) just needs to accept items at instanciation, optionaly an indexer with a getter only defined and a count property - and of course implemeting IEnumerable<T> so it can be enumerated. Therefore a simple wrapper around List<T> suffices:
public class ImmutableCollection : IEnumerable
{
private List innerList;public ImmutableCollection(IEnumerable collection) { innerList = new List(collection); } public T this\[int index\] { get { return innerList\[index\]; } } public int Count { get { return innerList.Count; } } public IEnumerator GetEnumerator() { return ((IEnumerable)innerList).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)innerList).GetEnumerator(); }
}
As the innerList is never exposed it is read only :) Usage:
ImmutableCollection collection = new ImmutableCollection(new string[] { "A", "B", "C" });
Of course, the properties/fields of the the items contained will not be immutable, but that is the same as the ReadOnlyCollection<T> available from
.AsReadOnly
, but the minimum functionality only is exposed, and whatever you need to add can be created simply by accessing the inner list. If necessary, the inner list could be madeprotected
so it can be accessed directly in derived classes where concrete classes are desired or required.Dave
Binging is like googling, it just feels dirtier. Please take your VB.NET out of our nice case sensitive forum. Astonish us. Be exceptional. (Pete O'Hanlon)
BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn) -
Well a simple collection (generic) that is immutable (so naturally read only) just needs to accept items at instanciation, optionaly an indexer with a getter only defined and a count property - and of course implemeting IEnumerable<T> so it can be enumerated. Therefore a simple wrapper around List<T> suffices:
public class ImmutableCollection : IEnumerable
{
private List innerList;public ImmutableCollection(IEnumerable collection) { innerList = new List(collection); } public T this\[int index\] { get { return innerList\[index\]; } } public int Count { get { return innerList.Count; } } public IEnumerator GetEnumerator() { return ((IEnumerable)innerList).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)innerList).GetEnumerator(); }
}
As the innerList is never exposed it is read only :) Usage:
ImmutableCollection collection = new ImmutableCollection(new string[] { "A", "B", "C" });
Of course, the properties/fields of the the items contained will not be immutable, but that is the same as the ReadOnlyCollection<T> available from
.AsReadOnly
, but the minimum functionality only is exposed, and whatever you need to add can be created simply by accessing the inner list. If necessary, the inner list could be madeprotected
so it can be accessed directly in derived classes where concrete classes are desired or required.Dave
Binging is like googling, it just feels dirtier. Please take your VB.NET out of our nice case sensitive forum. Astonish us. Be exceptional. (Pete O'Hanlon)
BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)+5 Thanks for this most enlightening answer !
When I consider the brief span of my life, swallowed up in the eternity before and after, the little space which I fill, and even can see, engulfed in the infinite immensity of spaces of which I am ignorant, and which knows me not, I am frightened, and am astonished at being here rather than there; for there is no reason why here rather than there, now rather than then.
Blaise Pascal
-
I have an abstract base class and two derived classes:-
MyBase
,MyFoo : MyBase
,MyBar : MyBase
. I am creating readonly (and immutable) collections ofMyFoo
andMyBar
:-MyFooCollection
,MyBarCollection
. These are created by a one shot only factory method, so once instanciated the collection's contents will not change. I would also like to provide, for convenience, a collection ofMyBase
:-MyBaseCollection
which is a collation of all items in the other collections. What would be your preferred method? A. Create aMyBaseCollection
on the fly each time it requested from the existing two collections. B. Cache aMyBaseCollection
and even though it is a duplication of the existing two collections. C. Cache only aMyBaseCollection
and create the other two collections on the fly from it. D. Something else...Dave
Binging is like googling, it just feels dirtier. Please take your VB.NET out of our nice case sensitive forum. Astonish us. Be exceptional. (Pete O'Hanlon)
BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)I know I'm late to the party here, but this question, and what's been exposed in the ensuing discussion, has "fit" with some aspects of use of classes I've been exploring. 1. I noted that in one of colleague PieBald's responses, he defined the Lists (alist, blist) that held collections of instances of classes A,B as static, and outside the scope of all three classes, which I found intriguing. 2. It seems to me that you could exploit the fact that each instantiation of Class A or B does invoke the constructor in class C:
private abstract class C
{
public static readonly List<A> alist = new List<A>();
public static readonly List<B> blist = new List<B>();
public static readonly List<C> clist = new List<C>();public string Name { get; private set; } public C(string Name) { this.Name = Name; clist.Add(this); if(this is A) { alist.Add(this as A); } else { blist.Add(this as B); } } public override string ToString() { return (this.Name); }
}
Using this implementation of Class C, you guarantee that all three lists are updated with each new instantiation of A or B. Disclaimers-Dunno's: 1. don't know if this would play nice with .NET 2.0 2. thread safety ? I've tested this code with the following:
A myA1 = new A("a1"); A myA2 = new A("a2"); B myB1 = new B("b1"); B myB2 = new B("b2"); foreach (var a in C.alist) { Console.WriteLine("This A's Name is : " + a.Name); } foreach (var b in C.blist) { Console.WriteLine("This B's Name is : " + b.Name); } foreach (var c in C.clist) { Console.WriteLine("This C is a Type: " + c.GetType() + " : Name = " + c.Name); }
The results were as expected:
This A's Name is : a1
This A's Name is : a2
This B's Name is : b1
This B's Name is : b2
This C is a Type: TestImmutable.Form1+A : Name = a1
This C is a Type: TestImmutable.Form1+A : Name = a2
This C is a Type: TestImmutable.Form1+B : Name = b1
This C is a Type: TestImmutable.Form1+B : Name = b2Be happy to have a critique of this approach. thanks, Bill
When I consider the brief span of my life, swallowed up in the eternity before and after, the little space which I fill, and even can see, engulfed in the infinite immensity of spaces of whi
-
An enumerator has a state ("where am I?") which must be saved somewhere. If its state is inside the collection itself, you probably can't have two independent iterators at once, so things like:
foreach(someType a in collection) {
foreach (someType b in collection) {
if (a!=b) doSomeThingTo(a,b);
}
}would fail. Having code accessing your collection from different threads through enumerators would fail too. What I'm saying is, most of the time,
IEnumerable
(i.e.GetEnumerator
) requires you to create a new enumerator each time. It is too bad they called it such,CreateEnumerator
would have been the better choice. :) PS, FWIW: are you familiar with AsReadOnly?[^]Luc Pattyn [My Articles] Nil Volentibus Arduum
How about getting the enumerator of each collection to enumerate both of them? I have knocked up an example of the general outline of my classes, I have used this in the
MyFooMyBarPool.MyBases
property.using System;
using System.Collections;
using System.Collections.Generic;public enum BaseType
{
MyFoo,
MyBar
}public abstract class MyBase
{
private BaseType baseType;
private string name;internal MyBase(BaseType baseType, string name) { this.baseType = baseType; this.name = name; } public string Name { get { return name; } } public override string ToString() { return string.Format("Type: {0}, Name: {1}", baseType, name); }
}
public sealed class MyFoo : MyBase
{
internal MyFoo(string name)
: base(BaseType.MyFoo, name)
{ }
}public sealed class MyBar : MyBase
{
internal MyBar(string name)
: base(BaseType.MyBar, name)
{ }
}public class SimpleCollection : IEnumerable
{
private List innerList;public SimpleCollection(IEnumerable items) { innerList = new List(items); } public T this\[int index\] { get { return innerList\[index\]; } } public int Count { get { return innerList.Count; } } public IEnumerator GetEnumerator() { return innerList.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return innerList.GetEnumerator(); }
}
public class MyFooCollection : SimpleCollection
{
internal MyFooCollection(IEnumerable myFoos)
: base(myFoos)
{ }
}public class MyBarCollection : SimpleCollection
{
internal MyBarCollection(IEnumerable myBars)
: base(myBars)
{ }
}// In my real code this is a singleton so there will only ever be one pool
public class MyFooMyBarPool
{
private MyFooCollection myFooCollection;
private MyBarCollection myBarCollection;public MyFooMyBarPool() { // Items and collections are instanciated here only. myFooCollection = new MyFooCollection(new MyFoo\[\] { new MyFoo("A"), new MyFoo("B"), new MyFoo("C") }); myBarCollection = new MyBarCollection(new MyBar\[\] { new MyBar("A"), new MyBar("B"), new MyBar("C") }); } public IEnumerable MyBases { get { IEnumerator myFooEnumer
-
Well a simple collection (generic) that is immutable (so naturally read only) just needs to accept items at instanciation, optionaly an indexer with a getter only defined and a count property - and of course implemeting IEnumerable<T> so it can be enumerated. Therefore a simple wrapper around List<T> suffices:
public class ImmutableCollection : IEnumerable
{
private List innerList;public ImmutableCollection(IEnumerable collection) { innerList = new List(collection); } public T this\[int index\] { get { return innerList\[index\]; } } public int Count { get { return innerList.Count; } } public IEnumerator GetEnumerator() { return ((IEnumerable)innerList).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)innerList).GetEnumerator(); }
}
As the innerList is never exposed it is read only :) Usage:
ImmutableCollection collection = new ImmutableCollection(new string[] { "A", "B", "C" });
Of course, the properties/fields of the the items contained will not be immutable, but that is the same as the ReadOnlyCollection<T> available from
.AsReadOnly
, but the minimum functionality only is exposed, and whatever you need to add can be created simply by accessing the inner list. If necessary, the inner list could be madeprotected
so it can be accessed directly in derived classes where concrete classes are desired or required.Dave
Binging is like googling, it just feels dirtier. Please take your VB.NET out of our nice case sensitive forum. Astonish us. Be exceptional. (Pete O'Hanlon)
BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)encapsulation :)
Luc Pattyn [My Articles] Nil Volentibus Arduum
-
How about getting the enumerator of each collection to enumerate both of them? I have knocked up an example of the general outline of my classes, I have used this in the
MyFooMyBarPool.MyBases
property.using System;
using System.Collections;
using System.Collections.Generic;public enum BaseType
{
MyFoo,
MyBar
}public abstract class MyBase
{
private BaseType baseType;
private string name;internal MyBase(BaseType baseType, string name) { this.baseType = baseType; this.name = name; } public string Name { get { return name; } } public override string ToString() { return string.Format("Type: {0}, Name: {1}", baseType, name); }
}
public sealed class MyFoo : MyBase
{
internal MyFoo(string name)
: base(BaseType.MyFoo, name)
{ }
}public sealed class MyBar : MyBase
{
internal MyBar(string name)
: base(BaseType.MyBar, name)
{ }
}public class SimpleCollection : IEnumerable
{
private List innerList;public SimpleCollection(IEnumerable items) { innerList = new List(items); } public T this\[int index\] { get { return innerList\[index\]; } } public int Count { get { return innerList.Count; } } public IEnumerator GetEnumerator() { return innerList.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return innerList.GetEnumerator(); }
}
public class MyFooCollection : SimpleCollection
{
internal MyFooCollection(IEnumerable myFoos)
: base(myFoos)
{ }
}public class MyBarCollection : SimpleCollection
{
internal MyBarCollection(IEnumerable myBars)
: base(myBars)
{ }
}// In my real code this is a singleton so there will only ever be one pool
public class MyFooMyBarPool
{
private MyFooCollection myFooCollection;
private MyBarCollection myBarCollection;public MyFooMyBarPool() { // Items and collections are instanciated here only. myFooCollection = new MyFooCollection(new MyFoo\[\] { new MyFoo("A"), new MyFoo("B"), new MyFoo("C") }); myBarCollection = new MyBarCollection(new MyBar\[\] { new MyBar("A"), new MyBar("B"), new MyBar("C") }); } public IEnumerable MyBases { get { IEnumerator myFooEnumer
Sorry Dave, it's too early a day to study all that in any detail. As I didn't see any new or Create in there (I may have overlooked them), I doubt it can be correct. If you want to offer simultaneous iterator-like operations to a collection, then the state of the iterator is bound to be outside said collection. You can simply test using this:
int count1=0;
foreach(someType element in collection) count1++;int count2=0;
foreach(someType element1 in collection) {
foreach(someType element2 in collection) {
count2++;
}
}
if (count2!=count1*count1) noGood();:)
Luc Pattyn [My Articles] Nil Volentibus Arduum
-
How about getting the enumerator of each collection to enumerate both of them? I have knocked up an example of the general outline of my classes, I have used this in the
MyFooMyBarPool.MyBases
property.using System;
using System.Collections;
using System.Collections.Generic;public enum BaseType
{
MyFoo,
MyBar
}public abstract class MyBase
{
private BaseType baseType;
private string name;internal MyBase(BaseType baseType, string name) { this.baseType = baseType; this.name = name; } public string Name { get { return name; } } public override string ToString() { return string.Format("Type: {0}, Name: {1}", baseType, name); }
}
public sealed class MyFoo : MyBase
{
internal MyFoo(string name)
: base(BaseType.MyFoo, name)
{ }
}public sealed class MyBar : MyBase
{
internal MyBar(string name)
: base(BaseType.MyBar, name)
{ }
}public class SimpleCollection : IEnumerable
{
private List innerList;public SimpleCollection(IEnumerable items) { innerList = new List(items); } public T this\[int index\] { get { return innerList\[index\]; } } public int Count { get { return innerList.Count; } } public IEnumerator GetEnumerator() { return innerList.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return innerList.GetEnumerator(); }
}
public class MyFooCollection : SimpleCollection
{
internal MyFooCollection(IEnumerable myFoos)
: base(myFoos)
{ }
}public class MyBarCollection : SimpleCollection
{
internal MyBarCollection(IEnumerable myBars)
: base(myBars)
{ }
}// In my real code this is a singleton so there will only ever be one pool
public class MyFooMyBarPool
{
private MyFooCollection myFooCollection;
private MyBarCollection myBarCollection;public MyFooMyBarPool() { // Items and collections are instanciated here only. myFooCollection = new MyFooCollection(new MyFoo\[\] { new MyFoo("A"), new MyFoo("B"), new MyFoo("C") }); myBarCollection = new MyBarCollection(new MyBar\[\] { new MyBar("A"), new MyBar("B"), new MyBar("C") }); } public IEnumerable MyBases { get { IEnumerator myFooEnumer
DaveyM69 wrote:
How about getting the enumerator
It seems you're still looking at it in the wrong way, when implemented properly there isn't just one enumerator to a collection, there are as many as you want (and that is why they need storage space outside the collection, hence a new action somewhere).
GetEnumerator
returns an enumerator, not the enumerator. :)Luc Pattyn [My Articles] Nil Volentibus Arduum
-
How about getting the enumerator of each collection to enumerate both of them? I have knocked up an example of the general outline of my classes, I have used this in the
MyFooMyBarPool.MyBases
property.using System;
using System.Collections;
using System.Collections.Generic;public enum BaseType
{
MyFoo,
MyBar
}public abstract class MyBase
{
private BaseType baseType;
private string name;internal MyBase(BaseType baseType, string name) { this.baseType = baseType; this.name = name; } public string Name { get { return name; } } public override string ToString() { return string.Format("Type: {0}, Name: {1}", baseType, name); }
}
public sealed class MyFoo : MyBase
{
internal MyFoo(string name)
: base(BaseType.MyFoo, name)
{ }
}public sealed class MyBar : MyBase
{
internal MyBar(string name)
: base(BaseType.MyBar, name)
{ }
}public class SimpleCollection : IEnumerable
{
private List innerList;public SimpleCollection(IEnumerable items) { innerList = new List(items); } public T this\[int index\] { get { return innerList\[index\]; } } public int Count { get { return innerList.Count; } } public IEnumerator GetEnumerator() { return innerList.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return innerList.GetEnumerator(); }
}
public class MyFooCollection : SimpleCollection
{
internal MyFooCollection(IEnumerable myFoos)
: base(myFoos)
{ }
}public class MyBarCollection : SimpleCollection
{
internal MyBarCollection(IEnumerable myBars)
: base(myBars)
{ }
}// In my real code this is a singleton so there will only ever be one pool
public class MyFooMyBarPool
{
private MyFooCollection myFooCollection;
private MyBarCollection myBarCollection;public MyFooMyBarPool() { // Items and collections are instanciated here only. myFooCollection = new MyFooCollection(new MyFoo\[\] { new MyFoo("A"), new MyFoo("B"), new MyFoo("C") }); myBarCollection = new MyBarCollection(new MyBar\[\] { new MyBar("A"), new MyBar("B"), new MyBar("C") }); } public IEnumerable MyBases { get { IEnumerator myFooEnumer
This is what Reflector gives for List<T> GetEnumerator():
public Enumerator GetEnumerator()
{
return new Enumerator((List<T>) this);
}Please notice the new keyword. :)
Luc Pattyn [My Articles] Nil Volentibus Arduum
-
This is what Reflector gives for List<T> GetEnumerator():
public Enumerator GetEnumerator()
{
return new Enumerator((List<T>) this);
}Please notice the new keyword. :)
Luc Pattyn [My Articles] Nil Volentibus Arduum
So when I call
myFooCollection.GetEnumerator();
which in turn callsinnerList.GetEnumerator();
whereinnerList
is aList<T>
I am actually getting a new enumertor anyway, and the same formyBarCollection.GetEnumerator();
, therefore there is no problem with:IEnumerator myFooEnumerator = myFooCollection.GetEnumerator();
while (myFooEnumerator.MoveNext())
yield return myFooEnumerator.Current;
IEnumerator myBarEnumerator = myBarCollection.GetEnumerator();
while (myBarEnumerator.MoveNext())
yield return myBarEnumerator.Current;as a new enumerator for the collections is created everytime?
Dave
Binging is like googling, it just feels dirtier. Please take your VB.NET out of our nice case sensitive forum. Astonish us. Be exceptional. (Pete O'Hanlon)
BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn) -
So when I call
myFooCollection.GetEnumerator();
which in turn callsinnerList.GetEnumerator();
whereinnerList
is aList<T>
I am actually getting a new enumertor anyway, and the same formyBarCollection.GetEnumerator();
, therefore there is no problem with:IEnumerator myFooEnumerator = myFooCollection.GetEnumerator();
while (myFooEnumerator.MoveNext())
yield return myFooEnumerator.Current;
IEnumerator myBarEnumerator = myBarCollection.GetEnumerator();
while (myBarEnumerator.MoveNext())
yield return myBarEnumerator.Current;as a new enumerator for the collections is created everytime?
Dave
Binging is like googling, it just feels dirtier. Please take your VB.NET out of our nice case sensitive forum. Astonish us. Be exceptional. (Pete O'Hanlon)
BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)That seems correct. And the unit test I suggested earlier confirms it. :)
Luc Pattyn [My Articles] Nil Volentibus Arduum