IEnumerable, лямбда-ковариация и методы расширения - PullRequest
1 голос
/ 31 марта 2020

У меня есть метод расширения для фильтрации типов, реализующих интерфейс, с использованием предиката, принимающего параметр, типизированный для этого интерфейса:

public interface ILifeTrack
{
    DateTimeOffset? CreatedOn { get; set; }
    DateTimeOffset? ModifiedOn { get; set; }
    DateTimeOffset? DeletedOn { get; set; }
}

public class LifeTrack : ILifeTrack
{
    public static Expression<Func<T, Boolean>> GetIsActiveExpression<T>()
    where T : ILifeTrack
    {
        return e => e.CreatedOn != null && e.DeletedOn == null;
    }

    public static readonly Expression<Func<ILifeTrack, Boolean>> isActiveExpression;
    public static readonly Func<ILifeTrack, Boolean> isActiveFunc;

    static LifeTrack()
    {
        isActiveExpression = GetIsActiveExpression<ILifeTrack>();
        isActiveFunc = isActiveExpression.Compile();
    }

    public DateTimeOffset? CreatedOn { get; set; }
    public DateTimeOffset? ModifiedOn { get; set; }
    public DateTimeOffset? DeletedOn { get; set; }
}


public static class IEnumerableOfILifeTrackExtensions
{
    public static IEnumerable<T> MetadataActive<T>(this IEnumerable<T> instance)
        where T : class, ILifeTrack
    {
        return instance.Where(LifeTrack.isActiveFunc);
    }
}

, но при использовании метода расширения возникает исключение, указывающее, что исходный IEnumerable <> имеет значение null:

System.ArgumentNullException: Value cannot be null.
Parameter name: source
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
...

Я точно знаю, что экземпляр, переданный методу расширения, не равен NULL, поскольку эти альтернативные формы работают, как и ожидалось:

instance.Where<T, T>(LifeTrack.isActiveFunc);
instance.Where(e => LifeTrack.isActiveFunc.Invoke(e));
Enumerable.Where<T>(instance, LifeTrack.isActiveFunc);

VS показывает, что

instance.Where(LifeTrack.isActiveFunc)

выводится на

instance.Where<T, ILifeTrack>(LifeTrack.isActiveFunc)

Теперь я думаю, что внутренне происходит какое-то кастинг и в какой-то момент провал даункастинга приводит к нулевой ссылке, но что на самом деле происходит за сцены?


Madreflection правильно, у меня был следующий метод расширения на месте, чтобы попытаться преодолеть ту же проблему:

    public static IEnumerable<T> Where<T, TI>(this IEnumerable<T> instance, Func<TI, bool> predicate)
    where T : class, TI
    {
        return Enumerable.Where(instance, predicate) as IEnumerable<T>;
    }

Ужасный контроль со стороны меня, но что это правильный способ решения проблемы использования ковариантного Fun c в. Где?

Я думаю, что это должно быть

instance.Where<T>(LifeTrack.isActiveFunc)

, но я все еще не понимаю хе это требует явного набора текста.

...