检查方法是否正在访问集合

时间:2017-04-03 19:18:22

标签: c# reflection c#-5.0

情况:

我被困在c#5.0中,直到2020年之前我无法在当前环境中升级到c#6.0。同时,代码库中的空值检查已经变得非常长,并且有时,在访问之前,不会检查正在访问的属性是否为null。前段时间我创建了一个TemporaryLanguageFeatures类,其中包含一些C#6.0行为的传真,如nameof。我决定创建一个NullPropagation方法,它接受Expression<Func<T>>并检查每个为null,然后再转到该系列中的下一个对象。

问题:

当表达式包含对集合的访问且集合为空或索引超出集合范围时,我得到相关的空集合或索引范围错误。我可以访问表达式,创建的LambdaExpression和已编译的委托/它的MethodInfo。 编辑:我知道c#6.0无法抵御这些情况,但在我的方法中,我仍然希望这样做。

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

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

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

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

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

当前代码:

var members = new List<Expression>();

......填写收藏品等

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

1 个答案:

答案 0 :(得分:0)

经过广泛研究后,我修复了索引访问问题,但是我选择不纠正linq问题,这就是原因:

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

  • 第一
  • 最后
  • 聚合
  • 最小
  • 最高
  • 平均

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

话虽如此,我确实纠正了索引访问问题,这里是解决方案,在if块里面的HIT!控制台写信是我的问题:

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