views:

113

answers:

2

What is the difference between ((IEnumerable)source).OfType<T>() and source as IEnumerable<T>

For me they look similar, but they are not!

source is of type IEnumerable<T>, but it is boxed as an object.

Edit

Here is some Code:

public class PagedList<T> : List<T>, IPagedList
{
    public PagedList(object source, int index, int pageSize, int totalCount)
    {
        if (source == null)
            throw new ArgumentNullException("The source is null!");


        // as IEnumerable<T> gives me only null

        IEnumerable<T> list = ((IEnumerable)source).OfType<T>();

        if (list == null)
            throw new ArgumentException(String.Format("The source is not of type {0}, the type is {1}", typeof(T).Name, source.GetType().Name));

        PagerInfo = new PagerInfo
                        {
                            TotalCount = totalCount,
                            PageSize = pageSize,
                            PageIndex = index,
                            TotalPages = totalCount / pageSize
                        };

        if (PagerInfo.TotalCount % pageSize > 0)
            PagerInfo.TotalPages++;

        AddRange(list);
    }

    public PagerInfo PagerInfo { get; set; }
}

On another place I create an instance of PagedList

public static object MapToPagedList<TSource, TDestination>(TSource model, int page, int pageSize, int totalCount) where TSource : IEnumerable
{
    var viewModelDestinationType = typeof(TDestination);
    var viewModelDestinationGenericType = viewModelDestinationType.GetGenericArguments().FirstOrDefault();

    var mappedList = MapAndCreateSubList(model, viewModelDestinationGenericType);

    Type listT = typeof(PagedList<>).MakeGenericType(new[] { viewModelDestinationGenericType });
    object list = Activator.CreateInstance(listT, new[] { (object) mappedList,  page, pageSize, totalCount });

    return list;
}

If anyone can tell me why I have to cast the mappedList to object, I would be really thankful :)

And here the MapAndCreateSubList method and the Map delegate:

private static List<object> MapAndCreateSubList(IEnumerable model, Type destinationType)
{
    return (from object obj in model select Map(obj, obj.GetType(), destinationType)).ToList();
}

 public static Func<object, Type, Type, object> Map = (a, b, c) =>
{
    throw new InvalidOperationException(
        "The Mapping function must be set on the AutoMapperResult class");
};
+3  A: 

OfType<T>() will only return the types inside the enumeration that are of type T. So if you have this

object[] myObjects = new object[] { 1, 2, "hi", "there" };

Then call

var myStrings = myObjects.OfType<string>();

Then myStrings will be an enumerable that will skip 1 and 2 and only return you "hi" and "there". You can't cast myObjects to IEnumerable<string>, because that's not what it is.

The other operator that is similar here is Cast<T>(), which will attempt to cast all of the items to type T.

   var myStrings = myObjects.Cast<string>();

Once you start iterating over myStrings in this case, you will get a InvalidCastException, because it will try to cast 1 to a string and fail.

Matt Greer
yes, but my types are all the same. is Cast<T> similar to as IEnumerable<T> ?
Rookian
+8  A: 

What is the difference between ((IEnumerable)source).OfType<T>() and source as IEnumerable<T> For me they look similar, but they are not!

You are right. They are very different.

The former means "take the source sequence and produce a brand new, different sequence composed of all the elements of the given type from the previous sequence".

The latter means "if the runtime type of the source sequence is of the given type then give me a reference to that sequence, otherwise give me null".

Let me illustrate with an example. Suppose you have:

IEnumerable<Animal> animals = new Animal[] { giraffe, tiger };
IEnumerable<Tiger> tigers = animals.OfType<Tiger>();

That will give you back a new, different sequence that contains a single tiger.

IEnumerable<Mammal> mammals = animals as IEnumerable<Mammal>;

That will give you null. Animals is NOT a sequence of mammals, even though it is a sequence of animals that happen to only be mammals. The actual runtime type of animals is "array of animal" and an array of animals is not type-compatible with a sequence of mammals. Why not? Well, suppose the conversion worked, and you then said:

animals[0] = snake;
Mammal mammal = mammals.First();

And hey, you just put a snake into a variable that can only contain a mammal! We cannot allow that, so the conversion does not work.

In C# 4 you can go the other way. You can do this:

IEnumerable<Object> objects = animals as IEnumerable<Object>;

because an array of animals can be treated as a sequence of objects. You put a snake in there, and a snake is still an object. This only works in C# 4 though. (And it only works if the two types are both reference types. You cannot turn an array of int into a sequence of object.)

But the key thing to understand is that the OfType<T> method returns a brand-new sequence, and the "as" operator does a runtime type test. Those are completely different things.

Here's another way to look at it.

tigers = animals.OfType<Tiger>() is basically the same as

tigers = animals.Where(x=>x is Tiger).Select(x=>(Tiger)x);

That is, produce a new sequence by doing a test of each member of animals to see if it is a tiger. If it is, cast it. If it is not, discard it.

mammals = animals as IEnumerable<Mammal> on the other hand, is basically the same as

if (animals is IEnumerable<Mammal>)
    mammals = (IEnumerable<Mammal>) animals;
else
    mammals = null;

Make sense?

Eric Lippert