首页 > 解决方案 > IEnumerable、lambda协方差和扩展方法

问题描述

我有一个扩展方法来过滤实现接口的类型,使用谓词将参数输入到该接口:

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<> 为空:

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

我确定传递给扩展方法的实例不为空,因为这些替代形式按预期工作:

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

我自己的严重疏忽,但是处理在 .Where 中使用协变 Func 的问题的正确方法是什么?

我认为应该是

instance.Where<T>(LifeTrack.isActiveFunc)

但我仍然不明白为什么它需要显式输入。

标签: c#linqextension-methodsienumerablecovariance

解决方案


推荐阅读