tags:

views:

213

answers:

7

I know the answer is not going to be simple, and I already use a couple of (I think ugly) cludges. I am simply looking for some elegant answers.

Abstract class:

public interface IOtherObjects;

public abstract class MyObjects<T> where T : IOtherObjects
{
   ...

   public List<T> ToList()
   {
       ...
   }
}

Children:

public class MyObjectsA : MyObjects<OtherObjectA> //(where OtherObjectA implements IOtherObjects)
{


}

public class MyObjectsB : MyObjects<OtherObjectB> //(where OtherObjectB implements IOtherObjects)
{


}

Is it possible, looping through a collection of MyObjects (or other similar grouping, generic or otherwise) to then utilise to ToList method of the MyObjects base class, as we do not specifically know the type of T at this point.

EDIT As for specific examples, whenever this has come up, I've thought about it for a while, and done something different instead, so there is no current requirement. but as it has come up quite frequently, I thought I would float it.

EDIT @Sara, it's not the specific type of the collection I care about, it could be a List, but still the ToList method of each instance is relatively unusable, without an anonymous type)

@aku, true, and this question may be relatively hypothetical, however being able to retrieve, and work with a list of T of objects, knowing only their base type would be very useful. Having the ToList returning a List Of BaseType has been one of my workarounds

EDIT @ all: So far, this has been the sort of discussion I was hoping for, though it largely confirms all I suspected. Thanks all so far, but anyone else, feel free to input.

EDIT@Rob, Yes it works for a defined type, but not when the type is only known as a List of IOtherObjects.

@Rob Again Thanks. That has usually been my cludgy workaround (no disrespect :) ). Either that or using the ConvertAll function to Downcast through a delegate. Thanks for taking the time to understand the problem.

QUALIFYING EDIT in case I have been a little confusing

To be more precise, (I may have let my latest implementation of this get it too complex):

lets say I have 2 object types, B and C inheriting from object A.

Many scenarios have presented themselves where, from a List of B or a List of C, or in other cases a List of either - but I don't know which if I am at a base class, I have needed a less specific List of A.

The above example was a watered-down example of the List Of Less Specific problem's latest incarnation.

Usually it has presented itself, as I think through possible scenarios that limit the amount of code that needs writing and seems a little more elegant than other options. I really wanted a discussion of possibilities and other points of view, which I have more or less got. I am surprised no one has mentioned ConvertAll() so far, as that is another workaround I have used, but a little too verbose for the scenarios at hand

@Rob Yet Again and Sara

Thanks, however I do feel I understand generics in all their static contexted glory, and did understand the issues at play here.

The actual design of our system and usage of generics it (and I can say this without only a touch of bias, as I was only one of the players in the design), has been done well. It is when I have been working with the core API, I have found situations when I have been in the wrong scope for doing something simply, instead I had to deal with them with a little less elegant than I like (trying either to be clever or perhaps lazy - I'll accept either of those labels).

My distaste for what I termed a cludge is largely that we require to do a loop through our record set simply to convert the objects to their base value which may be a performance hit.

I guess I was wondering if anyone else had come across this in their coding before, and if anyone had been cleverer, or at least more elegant, than me in dealing with it.

+2  A: 

In your case MyObjectsA and MyObjectsB don't have common predecessor. Generic class is template for different classes not a common base class. If you want to have common properties in different classes use interfaces. You can't call ToList in a loop cause it has different signature in different classes. You can create ToList that returns objects rather than specific type.

aku
Sure they do: "//(where OtherObjectA implements IOtherObjects)"
Frank Krueger
They do what? MyObjectA and MyObjectB are different classes. ToList is a part of abstract class. Please read question carefully before making such comments.
aku
Ahh, I misread your MyObjectsA as OtherObjectA. Apologies.
Frank Krueger
+2  A: 

why do you have a collection of MyObjects? Is there a specific reason you don't have a List?

Sara Chipps
In all, I have to agree. This is just bad design.
Frank Krueger
I think I would third this.. If my latest response is correct, I would not be happy with the design of it.
Rob Cooper
I am using the term Collection generically, not specifically
johnc
+1  A: 

You can still probably access the ToList() method, but since you are unsure of the type, won't this work?

foreach(var myObject in myObjectsList)
    foreach(var obj in myObject.ToList())
        //do something

Of course this will only work on C# 3.0.

Note that the use of var is merely to remove the requirement of knowing what type the lists contain; as opposed to Frank's comments that I have delusions that var will make typing dynamic.

Jon Limjap
`var` isn't magical. You can do this just as well in C# 2.0. Ask the for the type of `myObject` and `obj` and you'll see they're nothing special. `var` is not dynamic - it is static.
Frank Krueger
It is, but it removes the requirement for you to specify the type that you need, as opposed to typing MyObjectA myObject in myObjectsList.
Jon Limjap
Jon, nice trick, I completely forgot about var :)
aku
A: 

Generics are used for static time type checks not runtime dispatch. Use inheritance/interfaces for runtime dispatch, use generics for compile-time type guarantees.

interface IMyObjects : IEnumerable<IOtherObjects> {}
abstract class MyObjects<T> : IMyObjects where T : IOtherObjects {}

IEnumerable<IMyObjects> objs = ...;
foreach (IMyObjects mo in objs) {
    foreach (IOtherObjects oo in mo) {
        Console.WriteLine(oo);
    }
}

(Obviously, I prefer Enumerables over Lists.)

OR Just use a proper dynamic language like VB. :-)

Frank Krueger
+1  A: 

OK, I am confused, the following code works fine for me (curiosity got the better of me!):

// Original Code Snipped for Brevity - See Edit History if Req'd

Or have I missed something?

Update Following Response from OP

OK now I am really confused.. What you are saying is that you want to get a List of Typed values from a generic/abstract List? (the child classes therefore become irrelevant).

You cannot return a Typed List if the Types are children/interface implementors - they do not match! You can of course get a List of items that are of a specific type from the abstract List like so:

    public List<OfType> TypedList<OfType>() where OfType : IOtherObjects
    {
        List<OfType> rtn = new List<OfType>();

        foreach (IOtherObjects o in _objects)
        {
            Type objType = o.GetType();
            Type reqType = typeof(OfType);

            if (objType == reqType)
                rtn.Add((OfType)o);
        }

        return rtn;
    }

If I am still off-base here can you please reword your question?! (It doesn't seem like I am the only one unsure of what you are driving at). I am trying to establish if there is a misunderstanding of generics on your part.

Another Update :D

Right, so it looks like you want/need the option to get the typed List, or the base list yes?

This would make your abstract class look like this - you can use ToList to get the concrete type, or ToBaseList() to get a List of the interface type. This should work in any scenarios you have. Does that help?

public abstract class MyObjects<T> where T : IOtherObjects
{
    List<T> _objects = new List<T>();

    public List<T> ToList()
    {
        return _objects;
    }

    public List<IOtherObjects> ToBaseList()
    {
        List<IOtherObjects> rtn = new List<IOtherObjects>();
        foreach (IOtherObjects o in _objects)
        {
            rtn.Add(o);
        }
        return rtn;
    }
}

Update #3

It's not really a "cludgy" workaround (no disrespect taken) - thats the only way to do it.. I think the bigger issue here is a design/grok problem. You said you had a problem, this code solves it. But if you were expecting to do something like:

public abstract class MyObjects<T> where T : IOtherObjects
{
    List<T> _objects = new List<T>();

    public List<IOtherObjects> Objects
    { get { return _objects; } }
}
#warning This won't compile, its for demo's sake.

And be able to pick-and-choose the types that come out of it, how else could you do it?! I get the feeling you do not really understand what the point of generics are, and you are trying to get them to do something they are not designed for!?

Rob Cooper
His concern (I thought) was when he had a *collection* of MyObject<?>s, not a single MyObject<Whatever>.
Frank Krueger
+1  A: 

If you have

class B : A
class C : A

And you have

List<B> listB;
List<C> listC;

that you wish to treat as a List of the parent type

Then you should use

List<A> listA = listB.Cast<A>().Concat(listC.Cast<A>()).ToList()
David B
A: 

I have recently found the

List<A>.Cast<B>().ToList<B>()

pattern.

It does exactly what I was looking for,

johnc