views:

189

answers:

5

Is there a way to use a collection of a generic class, without supplying the underlying type ? Let's explain :

Here is what I'd like to have :

class TimeSerie<TValue> {
enter code here
}

List<TimeSerie<?>> blah;

Here is what I have to do so far :

class TimeSerie {}
class TypedTimeSerie<TValue> : TimeSerie {}

List<TimeSerie> blah;

So, any way to use the nice first solution ? (although I guess it would raise problems when trying to cast, for a loop for example ...)

+8  A: 

You can make your using code generic too... but at some point you do have to specify the type argument. You're not going to be able to create an instance of the generic type without the type argument being known. You can provide that information at execution time using reflection if you must, but it has to be there somehow.

Jon Skeet
To clarify, I think what Jon means is that instead of `List<TimeSerie<?>> blah;` you need `List<TimeSerie<T>> blah;` where T is a generic parameter of the class or method where this declaration appears.
Nate C-K
Am I the only one who sees an answer by Jon Skeet and give him a thumbs up without even reading the answer?
davidrobles
@David: Please don't do that! I make mistakes in answers every day... if they don't get picked up by the community, they'll stay wrong forever :(
Jon Skeet
@David Robles: probably, because even Jon isn't God, whatever some people may think ;-). NoFI Jon.
Webleeuw
@David: Apparently not. With respect to Jon, whose answers are usually very good, some of his answers seem to get a lot of votes for having 100k+ rep next to them while better answers languish near the bottom of the page. I would even go so far as to say that Jon answering a question might discourage others from answering who are motivated by the reputation system.
Matt Howells
you can do it without the type being specified by yourself directly, but the *compiler* must at some stage know it to assign it to a strongly typed variable or, as you say by reflection assigned to a wider variable.
ShuggyCoUk
+1  A: 

I dont see based on your question why you cannot derive your custom collection from ICollection<T> or List<T> (or maybe derive from ICollection and delegate the calls to a field of type List<T> you store internally?

(It's entirely possible I'm just not getting it, but can you give a small bit more sample code?)

Ruben Bartelink
+1  A: 

Why not ?

List<TimeSerie<Object>> blah;

Then after you specify your object. Also define your base class accordingly.

kurast
Hum, means that inheritance works recursively with generics, which I hadn't realized.Good to know !
Wam
this doesn't work: List<TimeSerie<Object>> blah = new List<TimeSerie<int>>(); causes Cannot implicitly convert type `System.Collections.Generic.List<TimeSerie<int>>' to `System.Collections.Generic.List<TimeSerie<object>>'(CS0029)]"
ShuggyCoUk
+1  A: 

Note that some 'mumbling' is possible in relation to anonymous types with c# thanks to two things:

  1. Type inference
  2. unification of identical anonymous types

If you are happy to rely on these two things remaining fixed (there are no guarantees on this, especially in relation to 2) then the following may be useful.

public static class Mumble 
{
    public static HashSet<T> HashSet<T>(T prototype)
    {
        return new HashSet<T>();
    }

    public static List<T> List<T>(T prototype)
    {
        return new List<T>();
    } 
}

You can use it like so:

var set = MumbleSet(new { Foo="", Bar="", Baz=0 });
var list = MumbleList(new { Foo="", Bar="", Baz=0 });
set.Add(new { Foo="x", Bar="y", Baz=1 });
set.Add(new { Foo="a", Bar="b", Baz=1 });
list.Add(new { Foo="a", Bar="b", Baz=1 });
var intersection = list.Intersect(set);
var concat = list.Concat(set);

This works well in cases where you have anonymous types you wish to populate into some other collection for use elsewhere within a method. A common use would be reading from a database query into a set for latter checking for existence within a loop where expressing this as a series of linq queries was either too cumbersome or too expensive.

For your motivating example you would have to add the following:

class TimeSerie<TValue> 
{   
    // or some other constructor equivalent 
    public TimeSerie(TValue value) { /* assign the value */ }
}

static class TimeSerieMumble
{
    public static TimeSerie<TValue> New<TValue>(TValue value)
    {
        return new TimeSerie<TValue>(value);
    }
}

Then you could use the code like so:

var tsList = Mumble.List(TimeSerieMumble.New(new { Name="", Value=0 }));
foreach (var x in from c select new { c.Name, c.Value })
{
    tsList.Add(TimeSerieMumble.New(new { x.Name, x.Value }));
}

Mumbling which 'leaks' into the public api is not feasible in c# 3.5 unless the type is to be mumbled through a series of type inferred generic methods in the same way as the above example. I have never seen a case where such a thing was useful given the resulting contortions required to the calling code. I would not think it would improve readability either. As a rule of thumb using more than the two levels of mumbling in the Name/Value example is likely to lead to serious complications down the line.

ShuggyCoUk
A: 

As others have said, there's no easy way to do this in C#. However, if it's really important, it is possible to faithfully encode this pattern using a few extra types, although it's a bit ugly:

interface ITimeSeriesUser<X> {
    X Use<T>(TimeSeries<T> series);
}

interface ITimeSeriesUser {
    void Use<T>(TimeSeries<T> series);
}

interface ITimeSeries {
    X Apply<X>(ITimeSeriesUser<X> user);
    void Apply(ITimeSeriesUser user);
}

class TimeSeries<T> : ITimeSeries {
    X Apply<X>(ITimeSeriesUser<X> user) { return user.Use(this); }
    void Apply(ITimeSeriesUser user) { return user.Use(this); }

    /* Your existing code goes here */
}

Now you can create a List<ITimeSeries> instance which holds TimeSeries<T> values regardless of their type arguments, and you can use ITimeSeriesUser implementations to manipulate them. Obviously this requires quite a bit of boilerplate, but if you need a faithful way to express the concept of a TimeSeries<?> then this may be your best bet.

kvb