views:

120

answers:

2

If I have a generic interface with a couple of implementing classes such as:

public interface IDataElement<T>
{
    int DataElement { get; set; }
    T Value { get; set; }
}

public class IntegerDataElement : IDataElement<int>
{
    public int DataElement { get; set; }
    public int Value { get; set; }
}

public class StringDataElement : IDataElement<String>
{
    public int DataElement { get; set; }
    public String Value { get; set; }
}

Is it possible to pass a collection of the implementing classes of differing types, without having to resort to passing as object.

It does not appear to be possible to define a return values as

public IDataElement<T>[] GetData()

or

public IDataElement<object>[] GetData() 

Any suggestions?

+3  A: 

You can certainly declare:

public IDataElement<T>[] GetData<T>()

and

public IDataElement<object>[] GetData()
  • although the latter probably isn't what you're after (your interface won't be variant even in C# 4 as it uses T in both an input and an output position; even if it were variant, you wouldn't be able to use that variance for value types). The former will require the caller to specify <T>, e.g.

    foo.GetData<string>();

Is that okay for you?

There's no way of expressing "a collection of object, each of which implements IDataElement<T> for a different T" unless you also give it a nongeneric base class, at which you could just use IList<IDataElement>. In this case the nongeneric IDataElement could have the DataElement property, leaving the Value property in the generic interface:

public interface IDataElement
{
    int DataElement { get; set; }
}

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

Is that useful in your particular situation?

It's not clear how you'd want to use a collection of data elements without knowing their types... if the above doesn't help you, maybe you could say more about what you expected to do with the collections.

Jon Skeet
I had tried the non-generic base class but it loses the detail of the Value property
benPearce
@benPearce: But you can't use that anyway, because you don't know the type involved. Please try to show how you'd use the code, and then we can advise you better.
Jon Skeet
I ended up going down the Non-generic base interface, with object GetValue() and SetValue(object) methods. Working so far
benPearce
+2  A: 

No you can't do this - the only options are to either use a non-generic interface:

public interface IDataElement
{
    int DataElement { get; set; }
    object Value { get; set; }
}

Alternatively create a wrapper and pass that to methods that know the types they require:

public class DataElementBag
{
    private IDictionary<Type, List<object>> _elements;
    ...
    public void Add<T>(IDataElement<T> de)
    {
        Type t = typeof(T);
        if(!this._elements.ContainsKey(t))
        {
            this._elements[t] = new List<object>();
        }

        this._elements[t].Add(de);
    }

    public void IEnumerable<IDataElement<T>> GetElementsByType<T>()
    {
        Type t = typeof(T);
        return this._elements.ContainsKey(t)
            ? this._elements[t].Cast<IDataElement<T>>()
            : Enumerable.Empty<T>();
    }
}
Lee