У меня есть метод расширения для фильтрации типов, реализующих интерфейс, с использованием предиката, принимающего параметр, типизированный для этого интерфейса:
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)
, но я все еще не понимаю хе это требует явного набора текста.