检查方法是否正在访问 collection

Check if method is accessing collection

情况:

我卡在 c# 5.0 中,直到 2020 年之后我才能在当前环境中升级到 c# 6.0。同时,代码库中的空检查变得非常长,有时正在访问的属性在被访问之前没有被检查是否为 null。不久前,我创建了一个 TemporaryLanguageFeatures class,其中包含一些 C# 6.0 行为的复制品,例如 nameof。我决定创建一个 NullPropagation 方法,该方法采用 Expression<Func<T>> 并检查每个是否为 null,然后再继续该系列中的下一个 object。

问题:

当表达式包含对collection的访问并且collection为空或索引超出[=41的范围时=],我得到相关的空 collection 或索引超出范围错误。我可以访问表达式、创建的 LambdaExpression 和编译的 delegate/its MethodInfo。 编辑:我知道 c#6.0 不能防止这些情况,但是在我的方法中我仍然希望这样做。

没有 try/catch 我需要检查给定的调用是否会产生错误:

A)索引超出范围(collection有10个成员,表达式如下):

var answer = NullPropagation(() => item.Location.PhoneNumbers[500]);

B) collection为空(collection没有成员,表达式如下):

var answer = NullPropagation(() => item.Location.PhoneNumbers.First());

当前代码:

var members = new List<Expression>();

...填写collection等

var previousMethod = (MethodInfo) null;
var nullFound = members.Any(x => 
{
    if (previousMethod != null && typeof(IEnumerable).IsAssignableFrom(previousMethod.ReturnType)) 
    {
        Console.WriteLine("HIT!");
        //Todo: Detect collection empty/index out of range, return true;
    }
    previousMethod = method;
    return Expression.Lambda(x).Compile().DynamicInvoke() == null;
}
...

经过广泛研究,我修复了索引访问问题,但我选择不更正 linq 问题,原因如下:

首先,可以检查该方法是否是以下任何可能由于空集合而引发错误的 linq 查询,然后检查集合上的 !.Any():

  • 第一个
  • 最后
  • 单身
  • 汇总
  • 分钟
  • 最大
  • 平均

这里的问题是,可以向其中任何一个提供谓词,以限制集合的方式仍然是 return 错误。处理这种情况的选项是有限的。我们需要使用 try/catch 来处理它(这是我想避免的事情)或在 IEnumerable Linq 扩展中重新创建内部逻辑,报告发现空值而不是抛出错误。任何一种解决方案都是 hacky,对我来说不值得。如果您正在阅读本文并对这种方法非常感兴趣,我可以提供我放在一起作为概念证明的代码。

话虽如此,我确实纠正了索引访问问题,这是该问题的解决方案,在 HIT 所在的 if 块内! console writeline 在我的问题中:

var collectionAccessMethod = method.Body as MethodCallExpression;
if (collectionAccessMethod != null &&
        previousMethod.ReturnType.GetProperties()
        .Where(p => p.GetIndexParameters().Any())
        .Select(p => p.GetGetMethod())
        .Contains(collectionAccessMethod.Method))
{
    var index = (int) Expression.Lambda(collectionAccessMethod.Arguments[0]).Compile().DynamicInvoke();
    var collection = ((Func<IEnumerable>)Expression.Lambda(previousMethod).Compile().DynamicInvoke()).Invoke();

    if (index > collection.Cast<object>().Count() - 1)
    {
        return true;
    }
}