How can you return a <ConcreteType> collection as an <Interface> collection?

I have a concrete class that contains a collection of another concrete class. I would like to expose both classes through interfaces, but I am having a hard time understanding how I can represent a Collection <ConcreteType> member as a Collection <Interface> member .

I am currently using .NET 2.0

The code below throws a compiler error:

Cannot implicitly convert type 'System.Collections.ObjectModel.Collection <nail>' to 'System.Collections.ObjectModel.Collection <INAIL>'

A commented attempt at throwing gives this compiler error:

Unable to convert type 'System.Collections.ObjectModel.Collection <nail>' for image 'System.Collections.ObjectModel.Collection <INAIL>' via reference conversion, boxing conversion, unboxing conversion, boxing conversion, or null conversion.

Is there a way to show a collection of specific types as a collection of interfaces, or do I need to create a new collection in the getter of the interface?

using System.Collections.ObjectModel;

public interface IBucket
{
    Collection<INail> Nails
    {
        get;
    }
}

public interface INail
{
}

internal sealed class Nail : INail
{
}

internal sealed class Bucket : IBucket
{
    private Collection<Nail> nails;

    Collection<INail> IBucket.Nails
    {
        get
        {
            //return (nails as Collection<INail>);
            return nails;
        }
    }

    public Bucket()
    {
        this.nails = new Collection<Nail>();
    }
}

      

+2


source to share


9 replies


C # does not support covariance on shared collections (only supported for arrays). In such cases, I use the adapter class. It just redirects all calls to the actual collection, converting the values ​​to the required type (doesn't require copying all the list values ​​to the new collection). Usage looks like this:



Collection<INail> IBucket.Nails
{
    get
    {
        return new ListAdapter<Nail, INail>(nails);
    }
}

    // my implementation (it incomplete)
    public class ListAdapter<T_Src, T_Dst> : IList<T_Dst>
{
    public ListAdapter(IList<T_Src> val)
    {
        _vals = val;
    }

    IList<T_Src> _vals;

    protected static T_Src ConvertToSrc(T_Dst val)
    {
        return (T_Src)((object)val);
    }

    protected static T_Dst ConvertToDst(T_Src val)
    {
        return (T_Dst)((object)val);
    }

    public void Add(T_Dst item)
    {
        T_Src val = ConvertToSrc(item);
        _vals.Add(val);
    }

    public void Clear()
    {
        _vals.Clear();
    }

    public bool Contains(T_Dst item)
    {
        return _vals.Contains(ConvertToSrc(item));
    }

    public void CopyTo(T_Dst[] array, int arrayIndex)
    {
        throw new NotImplementedException();
    }

    public int Count
    {
        get { return _vals.Count; }
    }

    public bool IsReadOnly
    {
        get { return _vals.IsReadOnly; }
    }

    public bool Remove(T_Dst item)
    {
        return _vals.Remove(ConvertToSrc(item));
    }

    public IEnumerator<T_Dst> GetEnumerator()
    {
        foreach (T_Src cur in _vals)
            yield return ConvertToDst(cur);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }

    public override string ToString()
    {
        return string.Format("Count = {0}", _vals.Count);
    }

    public int IndexOf(T_Dst item)
    {
        return _vals.IndexOf(ConvertToSrc(item));
    }

    public void Insert(int index, T_Dst item)
    {
        throw new NotImplementedException();
    }

    public void RemoveAt(int index)
    {
        throw new NotImplementedException();
    }

    public T_Dst this[int index]
    {
        get { return ConvertToDst(_vals[index]); }
        set { _vals[index] = ConvertToSrc(value); }
    }
}

      

-1


source


C # 3.0 generators are invariant. You cannot do this without creating a new object. C # 4.0 introduces safe covariance / contravariance, which won't change anything for read / write collections anyway (your case).



+6


source


Just define nails as

Collection<INail>

      

+6


source


Why not just return it as an interface, just use all your public methods in the interface, that way you don't have this problem, and if you later decide to return a different type of Nail class, then it will work fine.

+1


source


What version of .Net are you using?

If you are using .net 3.0+ you can only achieve this with System.Linq.

Check out this question which solved this for me.

0


source


There is one solution, which may not be exactly what you are asking for, but may be an acceptable alternative. Use arrays instead.

internal sealed class Bucket : IBucket
{
    private Nail[] nails;

    INail[] IBucket.Nails
    {
        get { return this.nails; }
    }

    public Bucket()
    {
        this.nails = new Nail[100];
    }
}

      

(If you end up doing something like this, keep this Framework design guide in mind. Usually arrays should not be exposed as properties, as they are usually copied before being returned to the caller, and copying is an expensive operation to do innocent property internally.)

0


source


use this as the body of your getter object:

List<INail> tempNails = new List<INail>();
foreach (Nail nail in nails)
{
    tempNails.Add(nail);
}
ReadOnlyCollection<INail> readOnlyTempNails = new ReadOnlyCollection<INail>(tempNails);
return readOnlyTempNails;

      

This is a bit of a hackish solution, but it does what you want.

Edited to return ReadOnlyCollection. Make sure to update your types in IBucket and Bucket.

0


source


You can add some generics. Fits better, tied stronger.

public interface IBucket<T> where T : INail
{
    Collection<T> Nails
    {
        get;
    }
}

public interface INail
{
}

internal sealed class Nail : INail
{
}

internal sealed class Bucket : IBucket<Nail>
{
    private Collection<Nail> nails;

    Collection<Nail> IBucket<Nail>.Nails
    {
        get
        {
            return nails; //works
        }
    }

    public Bucket()
    {
        this.nails = new Collection<Nail>();
    }
}

      

Thus Collection<Nail>

, the returned from the class Bucket

can only contain Nail

s. Anyone else INail

will not enter it. It may or may not be better depending on what you want.


Only if you want Collection<INail>

(interface property) you return from Bucket

in order to hold other INail

(than Nail

s) you can try below approach. But there is a problem. On the one hand, you say that you want to use the private Collection<Nail>

in class Bucket

, not Collection<INail>

because you don't want to accidentally add another INail

from class to it Bucket

, but on the other hand you will have to add another INail

from outside the class Bucket

. This is not possible in the same instance. The compiler stops you from accidentally adding any INail

to Collection<Nail>

. One way is to return another instance Collection<INail>

from your class Bucket

from an existing one Collection<Nail>

. This is less efficient, but it may be the semantics you are using. Please note that this is conceptually different from above

internal sealed class Bucket : IBucket
{
    private Collection<Nail> nails;

    Collection<INail> IBucket<Nail>.Nails
    {
        get
        {
            List<INail> temp = new List<INail>();
            foreach (Nail nail in nails)
                temp.Add(nail);

            return new Collection<INail>(temp);  
        }
    }

    public Bucket()
    {
        this.nails = new Collection<Nail>();
    }
}

      

0


source


you can use Cast extension

nails.Cast<INail>()

      

I cannot verify it here to provide a more complete example as we are using .NET 2.0 at work (gripe gripe), but I had a similar question here

-1


source







All Articles