Generic Collections and inheritance (again)

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Adam Clauss

    #1

    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<SubClasso fA> q) : base(q)
    {
    }
    }

    Unfortunately Queeu<SubClasso fA> 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<SubCl assofA>


    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<SubCl assofA>
    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



  • Andy

    #2
    Re: Generic Collections and inheritance (again)

    Is there anything stopping you from just accepting the superclass A?

    Comment

    • Adam Clauss

      #3
      Re: Generic Collections and inheritance (again)

      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" <ajj3085@alum.r it.edu> wrote in message
      news:1141403358 .078506.53120@p 10g2000cwp.goog legroups.com...[color=blue]
      > Is there anything stopping you from just accepting the superclass A?
      >[/color]


      Comment

      • Helge Jensen

        #4
        Re: Generic Collections and inheritance (again)



        Adam Clauss wrote:
        [color=blue]
        > Unfortunately Queeu<SubClasso fA> cannot be passed to a Queue<A>.[/color]

        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 ;)
        [color=blue]
        > 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<SubCl assofA>[/color]

        I use adapters for ICollection, assuming Bar is a subtype of Foo:

        ICollection<Bar > bars = ...;
        ICollection<Foo > = new UpcastedCollect ion<Bar,Foo>(ba rs);

        [color=blue]
        > 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<SubCl assofA>
        > 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?[/color]

        Well, actually yes, it makes sure no runtime casts are required.
        [color=blue]
        > As I see it now, my only option is to go back to the original Hashtable
        > object rather than Dictionary.[/color]

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

        #region Immutable Exceptions
        public class Immutable: InvalidOperatio nException {
        public readonly object Instance;
        protected Immutable(objec t instance): this(instance,
        string.Format(" {0} is immutable", instance)) { }
        protected Immutable(objec t instance, string msg): base(msg) {
        this.Instance = instance; }
        public static Immutable Make(object instance) { return new
        Immutable(insta nce); }
        public static Immutable<T> Make<T>(T instance) { return new
        Immutable<T>(in stance); }
        public static Immutable Make(object instance, string msg) { return
        new Immutable(insta nce, msg); }
        public static Immutable<T> Make<T>(T instance, string msg) { return
        new Immutable<T>(in stance, 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 TransformedColl ection<T, R> : ICollection<R>
        {
        public delegate R Transformer(T t);
        public readonly ICollection<T> Parent;
        public readonly Transformer Transform;
        public readonly TransformedColl ection<R, T>.Transformer Inverse;
        protected T NoInverse(R r) { throw new
        NotSupportedExc eption(string.F ormat("{0} does not implement {1}
        Inverse({2})", this, typeof(T), typeof(R))); }
        protected R NoTransform(T t) { throw new
        NotSupportedExc eption(string.F ormat("{0} does not implement {1}
        Transform({2})" , this, typeof(R), typeof(T))); }
        public TransformedColl ection(ICollect ion<T> parent, Transformer
        transform, TransformedColl ection<R, T>.Transformer inverse)
        {
        this.Parent = parent;
        this.Transform = transform == null ? NoTransform : transform;
        this.Inverse = inverse == null ? NoInverse : inverse;
        }
        public TransformedColl ection(ICollect ion<T> parent, Transformer
        transform): this(parent, transform, null) {}
        public TransformedColl ection(ICollect ion<T> parent,
        TransformedColl ection<R,T>.Tra nsformer inverse) : this(parent, null,
        inverse) { }
        public void Add(R r) { Parent.Add(Inve rse(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(I nverse(r)); }
        public int Count { get { return Parent.Count; } }
        public bool IsReadOnly { get { return Parent.IsReadOn ly; } }
        public IEnumerator<R> GetEnumerator() { foreach (T t in Parent)
        yield return Transform(t); }
        System.Collecti ons.IEnumerator
        System.Collecti ons.IEnumerable .GetEnumerator( ) { return GetEnumerator() ; }
        }
        public class UpcastedCollect ion<T, R> : ICollection<R> where T: R
        {
        public readonly ICollection<T> Parent;
        public UpcastedCollect ion(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.IsReadOn ly; } }
        public IEnumerator<R> GetEnumerator() { foreach (T t in Parent)
        yield return (R)t; }
        System.Collecti ons.IEnumerator
        System.Collecti ons.IEnumerable .GetEnumerator( ) { return GetEnumerator() ; }
        }
        public class AddItem<T> : ICollection<T>
        {
        public readonly ICollection<T> Parent;
        public readonly T Item;
        public readonly IEqualityCompar er<T> Comparer;
        public AddItem(ICollec tion<T> parent, T item, IEqualityCompar er<T>
        comparer)
        {
        this.Parent = parent;
        this.Item = item;
        this.Comparer = comparer == null ? EqualityCompare r<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(a rray, index);
        }
        public bool IsReadOnly { get { return Parent.IsReadOn ly; } }
        public IEnumerator<T> GetEnumerator()
        {
        yield return Item;
        foreach (T t in Parent)
        yield return t;
        }
        System.Collecti ons.IEnumerator
        System.Collecti ons.IEnumerable .GetEnumerator( ) { return GetEnumerator() ; }
        }
        #endregion

        #region IDictionary transformation
        public class TransformedDict ionary<K1, V1, K2, V2> : IDictionary<K2, V2>
        {
        public readonly IDictionary<K1, V1> Parent;
        public readonly TransformedColl ection<K1, K2>.Transformer KeyTransform;
        public readonly TransformedColl ection<K2, K1>.Transformer KeyInverse;
        public TransformedColl ection<V1, V2>.Transformer ValueTransform;
        public TransformedColl ection<V2, V1>.Transformer ValueInverse;
        public K2 DefaultKeyTrans form(K1 key) { throw new
        NotSupportedExc eption(string.F ormat("{0} does not implement {1}
        KeyTransform({2 })", this, typeof(K2), typeof(K1))); }
        public K1 DefaultKeyInver se(K2 key) { throw new
        NotSupportedExc eption(string.F ormat("{0} does not implement {1}
        KeyInversem({2} )", this, typeof(K1), typeof(K2))); }
        public V2 DefaultValueTra nsform(V1 value) { throw new
        NotSupportedExc eption(string.F ormat("{0} does not implement {1}
        ValueTransform( {2})", this, typeof(K2), typeof(K1))); }
        public V1 DefaultValueInv erse(V2 value) { throw new
        NotSupportedExc eption(string.F ormat("{0} does not implement {1}
        ValueInverse({2 })", this, typeof(K1), typeof(K2))); }
        public TransformedDict ionary(
        IDictionary<K1, V1> parent,
        TransformedColl ection<K1, K2>.Transformer keyTransform,
        TransformedColl ection<K2, K1>.Transformer keyInverse,
        TransformedColl ection<V1, V2>.Transformer valueTransform,
        TransformedColl ection<V2, V1>.Transformer valueInverse) {
        this.Parent = parent;
        this.KeyTransfo rm = keyTransform == null ? DefaultKeyTrans form :
        keyTransform;
        this.KeyInverse = keyInverse == null ? DefaultKeyInver se :
        keyInverse;
        this.ValueTrans form = valueTransform == null ?
        DefaultValueTra nsform : valueTransform;
        this.ValueInver se = valueInverse == null ? DefaultValueInv erse :
        valueInverse;
        }
        public bool ContainsKey(K2 key) { return
        Parent.Contains Key(KeyInverse( key)); }
        public void Add(K2 key, V2 value) { Parent.Add(KeyI nverse(key),
        ValueInverse(va lue)); }
        public bool Remove(K2 key) { return Parent.Remove(K eyInverse(key)) ; }
        public bool TryGetValue(K2 key, out V2 v2)
        {
        V1 v1;
        bool found = Parent.TryGetVa lue(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(va lue); }
        }
        public ICollection<K2> Keys { get { return new
        TransformedColl ection<K1, K2>(Parent.Keys , KeyTransform, KeyInverse); } }
        public ICollection<V2> Values { get { return new
        TransformedColl ection<V1, V2>(Parent.Valu es, ValueTransform,
        ValueInverse); } }
        public void Clear() { Parent.Clear(); }
        public void Add(KeyValuePai r<K2, V2> kvp) { Parent.Add(new
        KeyValuePair<K1 , V1>(KeyInverse( kvp.Key), ValueInverse(kv p.Value))); }
        public bool Contains(KeyVal uePair<K2, V2> kvp) { return
        Parent.Contains (new KeyValuePair<K1 ,V1>(KeyInverse (kvp.Key),
        ValueInverse(kv p.Value))); }
        public void CopyTo(KeyValue Pair<K2, V2>[] kvps, int index)
        {
        foreach (KeyValuePair<K 2, V2> kvp in this)
        kvps[index++] = kvp;
        }
        public bool Remove(KeyValue Pair<K2, V2> kvp) { return
        Parent.Remove(n ew KeyValuePair<K1 ,V1>(KeyInverse (kvp.Key),
        ValueInverse(kv p.Value))); }
        public int Count { get { return Parent.Count; } }
        public bool IsReadOnly { get { return Parent.IsReadOn ly; } }
        public IEnumerator<Key ValuePair<K2, V2>> GetEnumerator()
        {
        foreach (KeyValuePair<K 1, V1> kvp in Parent)
        yield return new KeyValuePair<K2 , V2>(KeyTransfor m(kvp.Key),
        ValueTransform( kvp.Value));
        }
        System.Collecti ons.IEnumerator
        System.Collecti ons.IEnumerable .GetEnumerator( ) { return GetEnumerator() ; }
        }
        public class FunctionDiction ary<K, V> : IDictionary<K, V>
        {
        public readonly ICollection<K> Keys;
        public readonly TransformedColl ection<K, V>.Transformer Function;
        public readonly IComparer<V> ValueComparer;
        public FunctionDiction ary(
        ICollection<K> keys,
        TransformedColl ection<K, V>.Transformer function,
        IComparer<V> valueComparer)
        {
        this.Keys = keys;
        this.Function = function;
        this.ValueCompa rer = valueComparer == null ? Comparer<V>.Def ault
        : valueComparer;
        }
        public bool ContainsKey(K key) { return Keys.Contains(k ey); }
        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(k ey);
        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
        TransformedColl ection<K, V>(Keys, Function); } }
        public V this[K key]
        {
        get
        {
        if (Keys.Contains( key))
        return Function(key);
        else
        throw new KeyNotFoundExce ption(string.Fo rmat("{0} not in
        {1}", key, this));
        }
        set { throw Immutable.Make( this); }
        }
        public void Add(KeyValuePai r<K, V> kvp) { throw Immutable.Make( this); }
        public void Clear() { throw Immutable.Make( this); }
        public bool Contains(KeyVal uePair<K, V> kvp) { return
        Keys.Contains(k vp.Key) && ValueComparer.C ompare(Function (kvp.Key),
        kvp.Value) == 0; }
        public void CopyTo(KeyValue Pair<K, V>[] kvps, int index)
        {
        foreach (K key in Keys)
        kvps[index++] = new KeyValuePair<K, V>(key, Function(key));
        }
        public bool Remove(KeyValue Pair<K, V> kvp) { throw
        Immutable.Make( this); }
        public int Count { get { return Keys.Count; } }
        public bool IsReadOnly { get { return true; } }
        public IEnumerator<Key ValuePair<K, V>> GetEnumerator()
        {
        foreach (K key in Keys)
        yield return new KeyValuePair<K, V>(key, Function(key));
        }
        System.Collecti ons.IEnumerator
        System.Collecti ons.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 IEqualityCompar er<K> KeyComparer;
        public readonly IEqualityCompar er<V> ValueComparer;
        public AddKeyValuePair (IDictionary<K, V> parent, K key, V value,
        IEqualityCompar er<K> keyComparer, IEqualityCompar er<V> valueComparer)
        {
        if (parent.Contain sKey(key))
        throw new ArgumentExcepti on("parent contains key");
        this.Parent = parent;
        this.Key = key;
        this.Value = value;
        this.KeyCompare r = keyComparer == null ?
        EqualityCompare r<K>.Default : keyComparer;
        this.ValueCompa rer = valueComparer == null ?
        EqualityCompare r<V>.Default : valueComparer;
        }
        public bool ContainsKey(K k) { return KeyComparer.Equ als(k, Key) ||
        Parent.Contains Key(k); }
        public void Add(K k, V v)
        {
        if (KeyComparer.Eq uals(k, Key))
        throw new ArgumentExcepti on(string.Forma t("{0} already
        contains {1}", this, k));
        else
        Parent.Add(k, v);
        }
        public bool Remove(K k)
        {
        if (KeyComparer.Eq uals(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.Equ als(k, Key) )
        {
        v = Value;
        return true;
        }
        else
        return Parent.TryGetVa lue(k, out v);
        }
        public V this[K k]
        {
        get
        {
        if ( KeyComparer.Equ als(k, Key) )
        return Value;
        else
        return Parent[k];
        }
        set {
        if (KeyComparer.Eq uals(k, Key))
        Value = value;
        else
        Parent[k] = value;
        }
        }
        public ICollection<K> Keys { get { return new
        AddItem<K>(Pare nt.Keys, Key, KeyComparer); } }
        public ICollection<V> Values { get { return new
        AddItem<V>(Pare nt.Values, Value, ValueComparer); } }
        public void Clear() { throw Immutable.Make( this,
        string.Format(" Cannot clear {0}", this)); }
        public void Add(KeyValuePai r<K, V> kvp)
        {
        if (KeyComparer.Eq uals(kvp.Key, Key))
        throw new ArgumentExcepti on(string.Forma t("{0} already
        contains {1}", this, kvp.Key));
        else
        Parent.Add(kvp) ;
        }
        public bool Contains(KeyVal uePair<K, V> kvp)
        {
        V v;
        return TryGetValue(kvp .Key, out v) &&
        ValueComparer.E quals(kvp.Value , v);
        }
        public void CopyTo(KeyValue Pair<K, V>[] kvps, int index)
        {
        kvps[index++] = new KeyValuePair<K, V>(Key, Value);
        Parent.CopyTo(k vps, index + 1);
        }
        public bool Remove(KeyValue Pair<K, V> kvp)
        {
        if (KeyComparer.Eq uals(kvp.Key, Key))
        throw Immutable.Make( this, string.Format(" Cannot remove key:
        {0}", kvp.Key));
        else
        return Parent.Remove(k vp);
        }
        public int Count { get { return Parent.Count + 1; } }
        public bool IsReadOnly { get { return Parent.IsReadOn ly; } }
        public IEnumerator<Key ValuePair<K, V>> GetEnumerator()
        {
        yield return new KeyValuePair<K, V>(Key, Value);
        foreach (KeyValuePair<K , V> kvp in Parent)
        yield return kvp;
        }
        System.Collecti ons.IEnumerator
        System.Collecti ons.IEnumerable .GetEnumerator( ) { return GetEnumerator() ; }
        }
        #endregion


        --
        Helge Jensen
        mailto:helge.je nsen@slog.dk
        sip:helge.jense n@slog.dk
        -=> Sebastian cover-music: http://ungdomshus.nu <=-

        Comment

        • Jon Skeet [C# MVP]

          #5
          Re: Generic Collections and inheritance (again)

          Adam Clauss <cabadam@tamu.e du> wrote:

          <snip>
          [color=blue]
          > Does that "read-only" aspect buy me any alternatives?[/color]

          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 - <skeet@pobox.co m>
          http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
          If replying to the group, please do not mail me too

          Comment

          Working...