tags:

views:

338

answers:

5

I am attempting to declare and use an interface like this:

public interface IItem<T>
{
  string Name { get; set; }
  T Value { get; set; }
}

This works fine until I attempt to create a list of these items. This fails to compile:

public interface IThing
{
    string Name { get; }
    IList<IItem<T>> ThingItems { get; }
}

so I am not certain where the issue is. The items value is not defined until runtime, and I need to have collections of the items. I figure that this is a fairly standard pattern, but I can not see where I am falling down.

+6  A: 

Your class needs to be generic too (Thing<T>) or else the list can't know what type to use.

public interface Thing<T>
{
    string Name { get; }
    IList<IItem<T>> thingItems { get; }
}

EDIT It compiles now.

EDIT It seems you want your IItem<T> to be in terms of any type. This won't work in C#. You could create IList> here, but that is less than ideal, since you loose your typing when you want to get the items out.

Brian Genisio
This won't compile!
Mehrdad Afshari
Won't compile. You can't have fields in an interface.
dirkgently
But this will not work in the case shown. The types of the items in thingItems vary based on the item, not the thing.
David Williams
Yeah, you're not going to get what you want with generics. Generics allow you to create a collection of objects that are the SAME object. Otherwise you're stuck using a higher level class (Object being the most generic) and casting them through some method to get the actual stored object.
GoingTharn
This is not a covariance/contravariance issue - it is just unknown what the concete type of Thing.thingItems will be at the point of creating a Thing instance.
Daniel Brückner
Daniel: Ya know, I think you are right. I had a solution in my head that wouldn't work because [co|contra]variance and I never put it down. Thanks. I will edit.
Brian Genisio
+2  A: 
  • Interfaces cannot contain fields (data members)
  • A type containing a generic type is also a generic type
dirkgently
+2  A: 

Two problems:

  1. You can't declare fields in an interface. (rationale: a field is considered an implementation detail, which is the thing interfaces are designed to abstract away)
  2. You can't have a generic field without specifying the type parameter (unless you have a type parameter on the declaring type too).
Mehrdad Afshari
+2  A: 

You're falling down because the compiler wants to know what kind of items are in your list. So if you don't know yet, just create a non-generic base interface, and derive a generic more specific interface:

Maybe this helps you out:

public interface IItem
{
  string Name { get; set; }
}

public interface IItem<T>: IItem
{
  T Value { get; set; }
}

public interface IThing
{
    string Name { get; }
    IList<IItem> Items { get; }
}

public interface IThing<T>: IThing
{
    string Name { get; }
    IList<IItem<T>> Items { get; }
}
Lucero
in your example, you'd want IItem to have the value property still, just return object.
Darren Kopp
That depends if you're gonna need it. Maybe you would also want to expose the Type for the item (e.g. what typeof(T) returns), etc.If there are non-generic methods on the interface (like, ProcessItem), you probably don't need the object value since they will be able to use the generic value in their implementation.
Lucero
This comes as close as I think I can. I just would never have the final interface you have here IThing<T> as the items vary indepenent of the things.
David Williams
That's fine too, of course. I added it so that the pattern is clear with non-generic and generic interfaces in such situations.
Lucero
A: 

When you create an instace of Thing you have to know what the type of Thing.thingItems is. So the following is the correct way.

public interface Thing<T>
{
    String Name { get; }
    IList<IItem<T>> thingItems { get; }
}

If you do not know the concret type at the point of instanciating Thing you can only use a common base class or a common interface for the type.

public interface Thing<T>
{
    String Name { get; }
    IList<IItem<ThingParameterBase>> thingItems { get; }
}

Or with a common interface.

public interface Thing<T>
{
    String Name { get; }
    IList<IItem<IThingParameter>> thingItems { get; }
}
Daniel Brückner
I could make it IList<IItem<object>> ThingItems I guess. I just feel that this smells to high heaven.
David Williams
...and it would give you covariance issues, since a IItem<object> is not compatible with IItem<AnyThingElse>!
Lucero