Brian Triplett
Brian Triplett

Reputation: 3532

Using Interface Type as generic

I have a class which must implement the following property

public ICollection<IType> Items
{
     get { return this.items;}
}

My question is how to implement this when the type of this.items is a List<MyType> where MyType implements IType. I need to ensure the following:

  1. Unecessary enumeration of the list is avoided if possible
  2. That the class can internally treat the elements of this.items as their concrete type
  3. That external callers may add and remove elements to this collection

Thanks in advance.

Upvotes: 1

Views: 120

Answers (5)

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112324

Either declare this.items as a List<IType> if you want to expose it as ICollection<IType> and thus allowing external callers to add ITypes that are not MyTypes.

Internally work like this on the items of the list

var myObj = this.items[i] as MyType;
if (myObj == null) {
    work with this.items[i] and treat it as a IType
} else {
    work with myObj which is a MyType
}

OR

declare the public property as

public ICollection<MyType> Items { get return this.items; } }

and thus allow external callers to add only items of type MyType.

I am sorry, but you cannot fulfill conditions (2) and (3) at the same time


UPDATE

Another option is to only allow external callers to get items of the list but not to add items, by using an indexer having only a getter.

public IType this[int i]
{
    get { return this.items[i]; }
}

an external caller can then access items like this

var obj = new ClassImplementingThisStuff();
int i = 5;
IType x = obj[i];

Also add a count property

public int Count { 
    get { return this items.Count; }
}

This solution avoids unnecessary enumeration.

Upvotes: 1

Wiktor Zychla
Wiktor Zychla

Reputation: 48230

How about Items being IEnumerable<IType>? IEnumerable is covariant so the code would just work with no changes. On the other hand, you could have another, dedicated method to add elements to the internal list.

class MainClass
{
  public static void Main()
  {
    ShowMeHowToDoIt show = new ShowMeHowToDoIt();
    show.Add( new TheType() );

    foreach ( var item in show.Items )
    {
   Console.WriteLine( item );
    }     
  }
}

public class ShowMeHowToDoIt
{
  private List<TheType> items = new List<TheType>();

  public void Add( TheType item ) { items.Add( item ); }

  public IEnumerable<IType> Items
  {
    get { return items; }
  }
}

public interface IType { }

public class TheType : IType { }

Upvotes: 1

kprobst
kprobst

Reputation: 16651

I think the points in the comments about this being possibly a bad design are valid, however you can still do something like this and get away with it:

interface IFruit
{
    string Name { get; }
    string SerialNumber { get; }
}

class Apple : IFruit
{

    private string _serial = Guid.NewGuid().ToString();

    public string Name {
        get {
            return "Apple";
        }
    }

    public string SerialNumber {
        get { return _serial; }
    }
}


class AppleBasket : IEnumerable<IFruit>
{
    private List<Apple> _items = new List<Apple>();

    public void Add(Apple apple) {
        _items.Add(apple);
    }


    public IEnumerator<IFruit> GetEnumerator() {
        return _items.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
        return _items.GetEnumerator();
    }
}

/******************/

AppleBasket basket = new AppleBasket();
Apple apple1 = new Apple();
basket.Add(apple1);

Apple apple2 = new Apple();
basket.Add(apple2);

foreach (IFruit fruit in basket) {
    Console.WriteLine(fruit.SerialNumber);
}

I would recommend you rethink your approach though.

Upvotes: 0

Brian Triplett
Brian Triplett

Reputation: 3532

As pointed out by @PaulPhillips in the comments to this question:

Requirements (2) and (3) are contradictory.

One approach is to change the type of Items to IEnumerable<IType> and have another property of ICollection<MyType>. This will mean some redesign but clearly I was going about this wrong anyway.

Thanks!

Upvotes: 1

Tim Copenhaver
Tim Copenhaver

Reputation: 3302

Like Paul mentioned, you can't have both #2 and #3. You'll have to pick one or the other, or expose the concrete type to external callers. But, for your actual requirement, your best bet is to store your collection as a List internally, and just use a method when you need to get a member by the concrete type. Something like this:

private List<IType> items = new List<IType>();

private TType GetItem<TType>(int index)
    where TType : IType
{
    return (TType)items[index];
}

public ICollection<IType> Items
{
    get
    {
        return this.items;
    }
}

Upvotes: 1

Related Questions