Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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");
};
like image 973
Rookian Avatar asked Dec 08 '22 01:12

Rookian


1 Answers

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?

like image 78
Eric Lippert Avatar answered Dec 09 '22 14:12

Eric Lippert