9

I'm trying to cast List<object> to List<string> dynamically. I've tried several ways, but I can't find a solution. This is a small sample that shows the problem:

List<object> listObject = new List<object>();
listObject.Add("ITEM 1");
listObject.Add("ITEM 2");
listObject.Add("ITEM 3");

List<string> listString = ¿¿listObject??;

Thanks in advance!

user7116
  • 63,008
  • 17
  • 141
  • 172
  • 8
    This won't be possible in version 4 either, because it is not typesafe to either "upcast" or "downcast" a `List` - it's invariant, not covariant or contravariant. By the way, I'm getting tired of this misinformation being posted to every question about collection co/contravariance. – Pavel Minaev Aug 21 '09 at 21:01

6 Answers6

18

If you can use LINQ then the Cast method will do what you need:

List<string> listString = listObject.Cast<string>().ToList();

You can also use the ConvertAll method, as Stan points out in his answer:

List<string> listString = listObject.ConvertAll(x => (string)x);

If you're not using C#3 then you'll need to use the "old" delegate syntax rather than a lambda:

List<string> listString =
    listObject.ConvertAll(delegate(object x) { return (string)x; });
LukeH
  • 263,068
  • 57
  • 365
  • 409
  • 1
    @nader: I always try to remember that when LINQ isn't available then neither is lambda syntax. – LukeH Aug 21 '09 at 21:00
  • And just to pre-empt any nitpickers... I'm aware that VS2008 can target earlier versions of the .NET framework, which would allow the use of lambda syntax but not LINQ. But, as a rule-of-thumb, no LINQ usually means no lambdas too. – LukeH Aug 22 '09 at 22:54
4

If you're using .NET 3.5 you can use, this way you don't have to do an extra ToList(). You can also supply your own converter if you need to convert advanced objects.

 List<string> listString = listObject.ConvertAll(x=> x as String);

If you can't use LINQ you can do this

foreach(object item in listObject)
{
  string convertedItem = item as String;
  if(convertedItem != null)
       listString.Add(convertedItem);
}
Stan R.
  • 15,757
  • 4
  • 50
  • 58
  • 1
    This is also supported in .NET 2.0 (without LINQ, though the delgate syntax is not as nice). One note though, I don't believe the .Cast.ToList() will create an "extra" list as you're implying. It will create one new list, for the result, as does your approach – Nader Shirazie Aug 21 '09 at 20:57
  • I think you mean C# 3.0 and .Net 3.5. – user7116 Aug 21 '09 at 20:59
  • @nader: I was implying that ToList() does an extra enumeration. – Stan R. Aug 21 '09 at 21:10
  • 1
    @Stan: Are you sure? ToList() will cause an enumeration, but that's it. Cast() will not actually enumerate the collection. – Nader Shirazie Aug 21 '09 at 21:35
  • @Nader: I am pretty sure Cast enumerates it once to perform the cast on each object and then ToList does another enumeration I believe (it calls CopyTo) – Stan R. Aug 22 '09 at 03:31
  • @Stan: The `Cast` method just returns an iterator object which does nothing until you actually start enumerating the sequence (with `ToList`, `foreach` etc), at which point it casts and streams one element at a time. The sequence is only enumerated once. – LukeH Aug 22 '09 at 23:02
  • @Stan: Having said that, although both the "`Cast` then `ToList`" approach and the `ConvertAll` approach only enumerate the sequence once, I'd expect `ConvertAll` to be marginally quicker because it loops through the source `List`'s underlying array directly rather than using an iterator object. (Though I doubt if this difference would be noticeable in most real-world scenarios.) – LukeH Aug 22 '09 at 23:14
1

How bout this:

public static List<T> ConvertToGenericList<T>(IList listOfObjects)
{
    List<T> items = new List<T>();

    for (int i = 0; i < listOfObjects.Count; i++)
    {
        items.Add((T)listOfObjects[i]);
    }
     return items;
}

Usage:

List<object> listObject = new List<object>();
listObject.Add("ITEM 1");
listObject.Add("ITEM 2");
listObject.Add("ITEM 3");
List<string> listString = Converter.ConvertToGenericList<string>(listObject);
Jon
  • 2,129
  • 7
  • 23
  • 31
0

I don't think you can do it one step. Instead, try something like this:

        List<object> listObject = new List<object>();
        listObject.Add( "ITEM 1" );
        listObject.Add( "ITEM 2" );
        listObject.Add( "ITEM 3" );

        List<string> lstStr = new List<string>( listObject.Count );

        foreach ( object obj in listObject )
        {
            lstStr.Add( obj.ToString() );
        }
TLiebe
  • 7,913
  • 1
  • 23
  • 28
  • this is wrong, he doesn't want toc all ToString() on thos object, he wants to convert it to a string object. Technically your example will work this time, but only for strings. But if he happens to bump into a more advanced object, he wont be able to use this method. – Stan R. Aug 21 '09 at 20:51
0
List<string> listString = (from o in listObject select (string)o).ToList();
Svante Svenson
  • 12,315
  • 4
  • 41
  • 45
-2

My first post... Hope usefull looks work in my project...

        public  dynamic ConvertList(Type CallingType)
    {

        dynamic DynamicList;

        if (CallingType == TypeOfValue)
        {
            Type d1 = typeof(List<>);

            Type[] typeArgs = { TypeOfValue };

            Type DynamicListType = d1.MakeGenericType(typeArgs);


            object DynamicListObj = Activator.CreateInstance(DynamicListType);


            DynamicList = Convert.ChangeType(DynamicListObj, DynamicListType);


            foreach (object ValueElement in ValueRange)
            {
                    dynamic el = Convert.ChangeType(ValueElement, TypeOfValue);
                    DynamicList.Add(el);
            }

        }
        else //retrun empty List but with right type
        {
            Type d1 = typeof(List<>);

            Type[] typeArgs = { CallingType };

            Type DynamicListType = d1.MakeGenericType(typeArgs);

            object DynamicListObj = Activator.CreateInstance(DynamicListType);

            DynamicList = Convert.ChangeType(DynamicListObj, DynamicListType);
        }

        return DynamicList;
    }

I think that I'll add also a try catch somewhere.

how to test

                if (PropertyType == typeof(UInt32))
                {
                    List<UInt32> UInt32_test = NewProperty.ConvertList(PropertyType);
                }
                if (PropertyType == typeof(string))
                {

                    List<string> string_test = NewProperty.ConvertList(PropertyType);
                }