В чем разница между ((IEnumerable) source) .OfType <T>() и источником как IEnumerable <T> - PullRequest
4 голосов
/ 18 сентября 2010

В чем разница между ((IEnumerable)source).OfType<T>() и source as IEnumerable<T>

Для меня они похожи, но это не так!

source относится к типу IEnumerable<T>, но помечается как object.

Редактировать

Вот код:

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; }
}

В другом месте я создаю экземпляр 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;
}

Если кто-нибудь может сказать мне, почему я должен привести mappedList к объекту, я был бы очень благодарен

А вот метод MapAndCreateSubList и делегат Map:

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");
};

Ответы [ 2 ]

12 голосов
/ 18 сентября 2010

В чем разница между ((IEnumerable)source).OfType<T>() и source as IEnumerable<T> Для меня они похожи, но это не так!

Вы правы. Они очень разные.

Первое означает «взять исходную последовательность и создать совершенно новую, отличающуюся последовательность, состоящую из всех элементов данного типа из предыдущей последовательности».

Последнее означает «если тип времени выполнения исходной последовательности имеет заданный тип, тогда дайте мне ссылку на эту последовательность, в противном случае дайте мне ноль».

Позвольте мне проиллюстрировать это примером. Предположим, у вас есть:

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

Это вернет вам новую, отличающуюся последовательность, содержащую одного тигра.

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

Это даст вам ноль. Животные - это НЕ последовательность млекопитающих, даже если это последовательность животных, которые оказываются только млекопитающими. Фактический тип животных во время выполнения - это «набор животных», а набор животных не совместим по типу с последовательностью млекопитающих. Почему бы и нет? Хорошо, предположим, что преобразование сработало, и вы тогда сказали:

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

И, эй, вы просто поместили змею в переменную, которая может содержать только млекопитающее! Мы не можем этого допустить, поэтому преобразование не работает.

В C # 4 вы можете пойти другим путем. Вы можете сделать это:

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

потому что массив животных может рассматриваться как последовательность объектов. Вы помещаете туда змею, а змея все еще является объектом. Это работает только в C # 4. (И это работает, только если оба типа являются ссылочными типами. Вы не можете превратить массив int в последовательность объектов.)

Но главное, что нужно понять, это то, что метод OfType<T> возвращает совершенно новую последовательность , а оператор "as" выполняет проверку типа времени выполнения . Это совершенно разные вещи.

Вот еще один способ взглянуть на это.

tigers = animals.OfType<Tiger>() в основном совпадает с

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

То есть создайте новую последовательность, выполнив проверку каждого члена животных, чтобы выяснить, является ли это тигром. Если это так, бросьте его. Если это не так, откажитесь от него.

mammals = animals as IEnumerable<Mammal>, с другой стороны, в основном совпадает с

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

Имеет смысл?

3 голосов
/ 18 сентября 2010

OfType<T>() будет возвращать только те типы внутри перечисления, которые относятся к типу T. Так что если у вас есть это

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

Тогда позвоните

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

Тогда myStrings будет перечислимым, который пропустит 1 и 2 и вернет вам только "привет" и "там". Вы не можете привести myObjects к IEnumerable<string>, потому что это не то, что есть.

Другой аналогичный здесь оператор - Cast<T>(), который попытается привести все предметы к типу T.

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

Как только вы начнете перебирать myStrings в этом случае, вы получите InvalidCastException, потому что он попытается привести 1 к строке и потерпит неудачу.

...