472,969 Members | 1,354 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 472,969 software developers and data experts.

Generic Collections and inheritance (again)

I ran into a problem a while back when attempting to convert existing .NET
1.1 based code to .NET 2.0 using Generic collections rather than Hashtable,
ArrayList, etc.

I ran into an issue because the old code allowed me to do what basically was
the following assignment:
class SomeClass
{
private Queue q;
SomeClass(Queue q)
{
this.q = q;
}
}

class SomeOtherClass : SomeClass
{
SomeOtherClass(Queue q) : base(q)
{
}
}
Where, when converted to generics is actually:
class SomeClass
{
private Queue<A> q;
SomeClass(Queue<A> q)
{
this.q = q;
}
}

class SomeOtherClass : SomeClass
{
SomeOtherClass(Queue<SubClassofA> q) : base(q)
{
}
}

Unfortunately Queeu<SubClassofA> cannot be passed to a Queue<A>.

Now, I was able to resolve the problem in this case by turning the class
which took this into a generic class itself:
class SomeClass<T> where T : A
and
class SomeOtherClass : SomeClass<SubClassofA>
However, I've now run into essentially the same situation with another
class. I do not believe I can use the same thing here though. The list
(Dictionary, actually) being passed into the class constructor could contain
various subclasses - not just one. Using the above solution of
class SomeOtherClass : SomeClass<SubClassofA>
limits me to ONLY using SubClassofA objects. But in this case there are
SEVERAL subclasses of A. I can guarrantee that the Dictionary here is ONLY
used in a "read" sense (the Dictionary is not altered). In fact, the only
time the list is used is to retrieve an element (which it currently expects
to be of type A). Various operations are then performed on that object, but
the list itself is never changed.

Does that "read-only" aspect buy me any alternatives?
As I see it now, my only option is to go back to the original Hashtable
object rather than Dictionary.

--
Adam Clauss

Mar 3 '06 #1
4 2178
Is there anything stopping you from just accepting the superclass A?

Mar 3 '06 #2
Yes - the fact that the original source is a Dictionary of SubclassOfA -
that Dictionary cannot simply be "passed" into a Dictionary of A. (Keys in
both cases are strings).

--
Adam Clauss

"Andy" <aj*****@alum.rit.edu> wrote in message
news:11*********************@p10g2000cwp.googlegro ups.com...
Is there anything stopping you from just accepting the superclass A?

Mar 3 '06 #3


Adam Clauss wrote:
Unfortunately Queeu<SubClassofA> cannot be passed to a Queue<A>.
Well it's not really unfortunate -- it preseves static type-safety,
consider the following:

Queue<SubA> subaq = new Queue<SubA>();
Queue<A> aq = subaq;
aq.Enque(new A()); // oops ;)
Now, I was able to resolve the problem in this case by turning the class
which took this into a generic class itself:
class SomeClass<T> where T : A
and
class SomeOtherClass : SomeClass<SubClassofA>
I use adapters for ICollection, assuming Bar is a subtype of Foo:

ICollection<Bar> bars = ...;
ICollection<Foo> = new UpcastedCollection<Bar,Foo>(bars);

However, I've now run into essentially the same situation with another
class. I do not believe I can use the same thing here though. The list
(Dictionary, actually) being passed into the class constructor could contain
various subclasses - not just one. Using the above solution of
class SomeOtherClass : SomeClass<SubClassofA>
limits me to ONLY using SubClassofA objects. But in this case there are
SEVERAL subclasses of A. I can guarrantee that the Dictionary here is ONLY
used in a "read" sense (the Dictionary is not altered). In fact, the only
time the list is used is to retrieve an element (which it currently expects
to be of type A). Various operations are then performed on that object, but
the list itself is never changed.

Does that "read-only" aspect buy me any alternatives?
Well, actually yes, it makes sure no runtime casts are required.
As I see it now, my only option is to go back to the original Hashtable
object rather than Dictionary.


Below are some adapters that shows what I mean and get you started on
writing your own adapters.

#region Immutable Exceptions
public class Immutable: InvalidOperationException {
public readonly object Instance;
protected Immutable(object instance): this(instance,
string.Format("{0} is immutable", instance)) { }
protected Immutable(object instance, string msg): base(msg) {
this.Instance = instance; }
public static Immutable Make(object instance) { return new
Immutable(instance); }
public static Immutable<T> Make<T>(T instance) { return new
Immutable<T>(instance); }
public static Immutable Make(object instance, string msg) { return
new Immutable(instance, msg); }
public static Immutable<T> Make<T>(T instance, string msg) { return
new Immutable<T>(instance, msg); }
}
public class Immutable<T>: Immutable {
public readonly new T Instance;
public Immutable(T instance, string msg) : base(instance, msg) {
this.Instance = instance; }
public Immutable(T instance) : this(instance, string.Format("{0} is
immutable", instance)) { }
}
#endregion

#region ICollection transformation
public class TransformedCollection<T, R> : ICollection<R>
{
public delegate R Transformer(T t);
public readonly ICollection<T> Parent;
public readonly Transformer Transform;
public readonly TransformedCollection<R, T>.Transformer Inverse;
protected T NoInverse(R r) { throw new
NotSupportedException(string.Format("{0} does not implement {1}
Inverse({2})", this, typeof(T), typeof(R))); }
protected R NoTransform(T t) { throw new
NotSupportedException(string.Format("{0} does not implement {1}
Transform({2})", this, typeof(R), typeof(T))); }
public TransformedCollection(ICollection<T> parent, Transformer
transform, TransformedCollection<R, T>.Transformer inverse)
{
this.Parent = parent;
this.Transform = transform == null ? NoTransform : transform;
this.Inverse = inverse == null ? NoInverse : inverse;
}
public TransformedCollection(ICollection<T> parent, Transformer
transform): this(parent, transform, null) {}
public TransformedCollection(ICollection<T> parent,
TransformedCollection<R,T>.Transformer inverse) : this(parent, null,
inverse) { }
public void Add(R r) { Parent.Add(Inverse(r)); }
public void Clear() { Parent.Clear(); }
public bool Contains(R r) { return Parent.Contains(Inverse(r)); }
public void CopyTo(R[] rs, int index) { foreach (T t in Parent)
rs[index++] = Transform(t); }
public bool Remove(R r) { return Parent.Remove(Inverse(r)); }
public int Count { get { return Parent.Count; } }
public bool IsReadOnly { get { return Parent.IsReadOnly; } }
public IEnumerator<R> GetEnumerator() { foreach (T t in Parent)
yield return Transform(t); }
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
public class UpcastedCollection<T, R> : ICollection<R> where T: R
{
public readonly ICollection<T> Parent;
public UpcastedCollection(ICollection<T> parent) { this.Parent =
parent; }
public void Add(R r) { Parent.Add((T)r); }
public void Clear() { Parent.Clear(); }
public bool Contains(R r) { return r is T &&
Parent.Contains((T)r); }
public void CopyTo(R[] rs, int index) { foreach (T t in Parent)
rs[index++] = (R)t; }
public bool Remove(R r) { return r is T && Parent.Remove((T)r); }
public int Count { get { return Parent.Count; } }
public bool IsReadOnly { get { return Parent.IsReadOnly; } }
public IEnumerator<R> GetEnumerator() { foreach (T t in Parent)
yield return (R)t; }
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
public class AddItem<T> : ICollection<T>
{
public readonly ICollection<T> Parent;
public readonly T Item;
public readonly IEqualityComparer<T> Comparer;
public AddItem(ICollection<T> parent, T item, IEqualityComparer<T>
comparer)
{
this.Parent = parent;
this.Item = item;
this.Comparer = comparer == null ? EqualityComparer<T>.Default :
comparer;
}
public void Add(T t) { Parent.Add(t); }
public bool Remove(T t)
{
if (!Parent.Remove(t))
throw Immutable.Make(this, string.Format("Cannot remove item
{0}", t));
else
return false;
}
public void Clear() { throw Immutable.Make(this,
string.Format("Cannot clear {0}", this)); }
public bool Contains(T t) { return Comparer.Equals(t, Item) ||
Parent.Contains(t); }
public int Count { get { return Parent.Count + 1; } }
public void CopyTo(T[] array, int index)
{
array[index++] = Item;
Parent.CopyTo(array, index);
}
public bool IsReadOnly { get { return Parent.IsReadOnly; } }
public IEnumerator<T> GetEnumerator()
{
yield return Item;
foreach (T t in Parent)
yield return t;
}
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
#endregion

#region IDictionary transformation
public class TransformedDictionary<K1, V1, K2, V2> : IDictionary<K2, V2>
{
public readonly IDictionary<K1, V1> Parent;
public readonly TransformedCollection<K1, K2>.Transformer KeyTransform;
public readonly TransformedCollection<K2, K1>.Transformer KeyInverse;
public TransformedCollection<V1, V2>.Transformer ValueTransform;
public TransformedCollection<V2, V1>.Transformer ValueInverse;
public K2 DefaultKeyTransform(K1 key) { throw new
NotSupportedException(string.Format("{0} does not implement {1}
KeyTransform({2})", this, typeof(K2), typeof(K1))); }
public K1 DefaultKeyInverse(K2 key) { throw new
NotSupportedException(string.Format("{0} does not implement {1}
KeyInversem({2})", this, typeof(K1), typeof(K2))); }
public V2 DefaultValueTransform(V1 value) { throw new
NotSupportedException(string.Format("{0} does not implement {1}
ValueTransform({2})", this, typeof(K2), typeof(K1))); }
public V1 DefaultValueInverse(V2 value) { throw new
NotSupportedException(string.Format("{0} does not implement {1}
ValueInverse({2})", this, typeof(K1), typeof(K2))); }
public TransformedDictionary(
IDictionary<K1, V1> parent,
TransformedCollection<K1, K2>.Transformer keyTransform,
TransformedCollection<K2, K1>.Transformer keyInverse,
TransformedCollection<V1, V2>.Transformer valueTransform,
TransformedCollection<V2, V1>.Transformer valueInverse) {
this.Parent = parent;
this.KeyTransform = keyTransform == null ? DefaultKeyTransform :
keyTransform;
this.KeyInverse = keyInverse == null ? DefaultKeyInverse :
keyInverse;
this.ValueTransform = valueTransform == null ?
DefaultValueTransform : valueTransform;
this.ValueInverse = valueInverse == null ? DefaultValueInverse :
valueInverse;
}
public bool ContainsKey(K2 key) { return
Parent.ContainsKey(KeyInverse(key)); }
public void Add(K2 key, V2 value) { Parent.Add(KeyInverse(key),
ValueInverse(value)); }
public bool Remove(K2 key) { return Parent.Remove(KeyInverse(key)); }
public bool TryGetValue(K2 key, out V2 v2)
{
V1 v1;
bool found = Parent.TryGetValue(KeyInverse(key), out v1);
if (found)
v2 = ValueTransform(v1);
else
v2 = default(V2);
return found;
}
public V2 this[K2 key]
{
get { return ValueTransform(Parent[KeyInverse(key)]); }
set { Parent[KeyInverse(key)] = ValueInverse(value); }
}
public ICollection<K2> Keys { get { return new
TransformedCollection<K1, K2>(Parent.Keys, KeyTransform, KeyInverse); } }
public ICollection<V2> Values { get { return new
TransformedCollection<V1, V2>(Parent.Values, ValueTransform,
ValueInverse); } }
public void Clear() { Parent.Clear(); }
public void Add(KeyValuePair<K2, V2> kvp) { Parent.Add(new
KeyValuePair<K1, V1>(KeyInverse(kvp.Key), ValueInverse(kvp.Value))); }
public bool Contains(KeyValuePair<K2, V2> kvp) { return
Parent.Contains(new KeyValuePair<K1,V1>(KeyInverse(kvp.Key),
ValueInverse(kvp.Value))); }
public void CopyTo(KeyValuePair<K2, V2>[] kvps, int index)
{
foreach (KeyValuePair<K2, V2> kvp in this)
kvps[index++] = kvp;
}
public bool Remove(KeyValuePair<K2, V2> kvp) { return
Parent.Remove(new KeyValuePair<K1,V1>(KeyInverse(kvp.Key),
ValueInverse(kvp.Value))); }
public int Count { get { return Parent.Count; } }
public bool IsReadOnly { get { return Parent.IsReadOnly; } }
public IEnumerator<KeyValuePair<K2, V2>> GetEnumerator()
{
foreach (KeyValuePair<K1, V1> kvp in Parent)
yield return new KeyValuePair<K2, V2>(KeyTransform(kvp.Key),
ValueTransform(kvp.Value));
}
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
public class FunctionDictionary<K, V> : IDictionary<K, V>
{
public readonly ICollection<K> Keys;
public readonly TransformedCollection<K, V>.Transformer Function;
public readonly IComparer<V> ValueComparer;
public FunctionDictionary(
ICollection<K> keys,
TransformedCollection<K, V>.Transformer function,
IComparer<V> valueComparer)
{
this.Keys = keys;
this.Function = function;
this.ValueComparer = valueComparer == null ? Comparer<V>.Default
: valueComparer;
}
public bool ContainsKey(K key) { return Keys.Contains(key); }
public void Add(K key, V value) { throw Immutable.Make(this); }
public bool Remove(K key) { throw Immutable.Make(this); }
public bool TryGetValue(K key, out V v)
{
bool found = Keys.Contains(key);
if (found)
v = Function(key);
else
v = default(V);
return found;
}
ICollection<K> IDictionary<K, V>.Keys { get { return Keys; } }
public ICollection<V> Values { get { return new
TransformedCollection<K, V>(Keys, Function); } }
public V this[K key]
{
get
{
if (Keys.Contains(key))
return Function(key);
else
throw new KeyNotFoundException(string.Format("{0} not in
{1}", key, this));
}
set { throw Immutable.Make(this); }
}
public void Add(KeyValuePair<K, V> kvp) { throw Immutable.Make(this); }
public void Clear() { throw Immutable.Make(this); }
public bool Contains(KeyValuePair<K, V> kvp) { return
Keys.Contains(kvp.Key) && ValueComparer.Compare(Function(kvp.Key),
kvp.Value) == 0; }
public void CopyTo(KeyValuePair<K, V>[] kvps, int index)
{
foreach (K key in Keys)
kvps[index++] = new KeyValuePair<K, V>(key, Function(key));
}
public bool Remove(KeyValuePair<K, V> kvp) { throw
Immutable.Make(this); }
public int Count { get { return Keys.Count; } }
public bool IsReadOnly { get { return true; } }
public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
{
foreach (K key in Keys)
yield return new KeyValuePair<K, V>(key, Function(key));
}
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
public class AddKeyValuePair<K, V> : IDictionary<K, V>
{
public readonly IDictionary<K, V> Parent;
public readonly K Key;
public V Value;
public readonly IEqualityComparer<K> KeyComparer;
public readonly IEqualityComparer<V> ValueComparer;
public AddKeyValuePair(IDictionary<K, V> parent, K key, V value,
IEqualityComparer<K> keyComparer, IEqualityComparer<V> valueComparer)
{
if (parent.ContainsKey(key))
throw new ArgumentException("parent contains key");
this.Parent = parent;
this.Key = key;
this.Value = value;
this.KeyComparer = keyComparer == null ?
EqualityComparer<K>.Default : keyComparer;
this.ValueComparer = valueComparer == null ?
EqualityComparer<V>.Default : valueComparer;
}
public bool ContainsKey(K k) { return KeyComparer.Equals(k, Key) ||
Parent.ContainsKey(k); }
public void Add(K k, V v)
{
if (KeyComparer.Equals(k, Key))
throw new ArgumentException(string.Format("{0} already
contains {1}", this, k));
else
Parent.Add(k, v);
}
public bool Remove(K k)
{
if (KeyComparer.Equals(k, Key))
throw Immutable.Make(this, string.Format("Cannot remove key:
{0}", k));
else
return Parent.Remove(k);
}
public bool TryGetValue(K k, out V v)
{
if ( KeyComparer.Equals(k, Key) )
{
v = Value;
return true;
}
else
return Parent.TryGetValue(k, out v);
}
public V this[K k]
{
get
{
if ( KeyComparer.Equals(k, Key) )
return Value;
else
return Parent[k];
}
set {
if (KeyComparer.Equals(k, Key))
Value = value;
else
Parent[k] = value;
}
}
public ICollection<K> Keys { get { return new
AddItem<K>(Parent.Keys, Key, KeyComparer); } }
public ICollection<V> Values { get { return new
AddItem<V>(Parent.Values, Value, ValueComparer); } }
public void Clear() { throw Immutable.Make(this,
string.Format("Cannot clear {0}", this)); }
public void Add(KeyValuePair<K, V> kvp)
{
if (KeyComparer.Equals(kvp.Key, Key))
throw new ArgumentException(string.Format("{0} already
contains {1}", this, kvp.Key));
else
Parent.Add(kvp);
}
public bool Contains(KeyValuePair<K, V> kvp)
{
V v;
return TryGetValue(kvp.Key, out v) &&
ValueComparer.Equals(kvp.Value, v);
}
public void CopyTo(KeyValuePair<K, V>[] kvps, int index)
{
kvps[index++] = new KeyValuePair<K, V>(Key, Value);
Parent.CopyTo(kvps, index + 1);
}
public bool Remove(KeyValuePair<K, V> kvp)
{
if (KeyComparer.Equals(kvp.Key, Key))
throw Immutable.Make(this, string.Format("Cannot remove key:
{0}", kvp.Key));
else
return Parent.Remove(kvp);
}
public int Count { get { return Parent.Count + 1; } }
public bool IsReadOnly { get { return Parent.IsReadOnly; } }
public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
{
yield return new KeyValuePair<K, V>(Key, Value);
foreach (KeyValuePair<K, V> kvp in Parent)
yield return kvp;
}
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
#endregion
--
Helge Jensen
mailto:he**********@slog.dk
sip:he**********@slog.dk
-=> Sebastian cover-music: http://ungdomshus.nu <=-
Mar 3 '06 #4
Adam Clauss <ca*****@tamu.edu> wrote:

<snip>
Does that "read-only" aspect buy me any alternatives?


Yes. You can write a generic wrapper class which implements
IDictionary<T> and delegates all the reading to an IDictionary<U> where
U : T (where this dictionary would be supplied at construction), and
throws an exception for all "writing" operations. You'll have to write
wrappers for iteration etc as well, unfortunately.

My guess is that someone else has already written such wrappers, but I
don't know of any unfortunately.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Mar 4 '06 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

18
by: Steven Bethard | last post by:
In the "empty classes as c structs?" thread, we've been talking in some detail about my proposed "generic objects" PEP. Based on a number of suggestions, I'm thinking more and more that instead of...
2
by: Jasper Kent | last post by:
I'm trying to do the equivalent of using typedefs with templates in C++ to avoid long instantiation names. I can do this okay: using BigDictionary = System.Collections.Generic.Dictionary<int,...
22
by: Adam Clauss | last post by:
OK, I have class A defined as follows: class A { A(Queue<B> queue) { ... } } Now, I then have a subclass of both classes A and B. The subclass of A (SubA), more specifically is passed a...
2
by: Harold Howe | last post by:
Howdy all, I am getting a compiler error regarding a consrained conversion. It complains that it can't make the type conversion, even though the generic type argument inherits from the target of...
25
by: Lars | last post by:
Hi, I have a base class holding a generic list that needs to be accessed by both the base class and its subclasses. What is the best solution to this? I am fairly new to generics, but I am...
2
by: Angel Mateos | last post by:
I have this structure: Class ElemBase Class Elem1 : Inherits ElemBase Class ColecBase(Of GenElem As {ElemBase, New}) : Inherits System.ComponentModel.BindingList(Of GenElem) Class Colec1...
4
by: =?Utf-8?B?QkogU2FmZGll?= | last post by:
We have a class that has a public property that is of type List<T>. FXCop generates a DoNotExposeGenericLists error, indicating "System.Collections.Generic.List<Tis a generic collection designed...
13
by: rkausch | last post by:
Hello everyone, I'm writing because I'm frustrated with the implementation of C#'s generics, and need a workaround. I come from a Java background, and am currently writing a portion of an...
2
by: SimonDotException | last post by:
I am trying to use reflection in a property of a base type to inspect the properties of an instance of a type which is derived from that base type, when the properties can themselves be instances of...
0
by: lllomh | last post by:
Define the method first this.state = { buttonBackgroundColor: 'green', isBlinking: false, // A new status is added to identify whether the button is blinking or not } autoStart=()=>{
2
by: DJRhino | last post by:
Was curious if anyone else was having this same issue or not.... I was just Up/Down graded to windows 11 and now my access combo boxes are not acting right. With win 10 I could start typing...
0
by: Aliciasmith | last post by:
In an age dominated by smartphones, having a mobile app for your business is no longer an option; it's a necessity. Whether you're a startup or an established enterprise, finding the right mobile app...
0
tracyyun
by: tracyyun | last post by:
Hello everyone, I have a question and would like some advice on network connectivity. I have one computer connected to my router via WiFi, but I have two other computers that I want to be able to...
4
NeoPa
by: NeoPa | last post by:
Hello everyone. I find myself stuck trying to find the VBA way to get Access to create a PDF of the currently-selected (and open) object (Form or Report). I know it can be done by selecting :...
3
NeoPa
by: NeoPa | last post by:
Introduction For this article I'll be using a very simple database which has Form (clsForm) & Report (clsReport) classes that simply handle making the calling Form invisible until the Form, or all...
1
by: Teri B | last post by:
Hi, I have created a sub-form Roles. In my course form the user selects the roles assigned to the course. 0ne-to-many. One course many roles. Then I created a report based on the Course form and...
3
by: nia12 | last post by:
Hi there, I am very new to Access so apologies if any of this is obvious/not clear. I am creating a data collection tool for health care employees to complete. It consists of a number of...
3
by: GKJR | last post by:
Does anyone have a recommendation to build a standalone application to replace an Access database? I have my bookkeeping software I developed in Access that I would like to make available to other...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.