Weird test coverage results for iterator block, why are these statements not executed?

戏子无情 提交于 2019-12-03 05:35:25

One of the problems with iterator methods is that the compiler generates a rather large and complex state machine to manage the deferred execution of the code within the iterator method. This usually generates a class or two. These classes are meant to deal with the general case and not your specific case, so there's likely at least a bit of code in there that is never used. You can look at what is generated by looking at your assembly with tools like ILSpy, JustDecompile or Reflector. It will show the classes in your assembly generated by the C# compiler (usually class names containing '<', etc.)

What the profiler knows about is how the PDB associates to your code and despite the possibility that all of the code you wrote possibly being executed, there's still a possibility that not all of the code generated by the compiler got executed. The profiler probably doesn't know this and simply says that a certain percentage (less than 100) of a particular iterator method got executed.

One of the things likely getting generated is exception handling code. Because the compiler doesn't know your code won't or possibly can't generate an exception it will still generate code to compensate for an exception--it needs to keep it's state from becoming corrupt. I bet if you included a way to throw an exception in various places in your iterator method based on some flag and ran the method twice (once without exceptions and once with exceptions in the same run) that the percentages would be different--likely higher because the generated exception handling code would then be exercised.

The fact that the end of the method "seems" to not be executed is likely because that code is part of a different method in the state machine that gets executed and the compiler never generates an association from that generated code to the code in your class.

UPDATE: to get a better understanding of what the compiler is doing and see an example of the type of code it generates see section 10.14 Iterators in the C# spec (http://www.microsoft.com/en-us/download/details.aspx?id=7029)

In addition to your question and the in detail answer, I had the following behaviour.

    // less than 100% coverage
    public static IEnumerable<T> ForEachYieldDo<T>(this IEnumerable<T> source, Action<T> action)
    {
        foreach (var x in source)
        {
            action(x);
            yield return x;
        }
    }

    // 100% code coverage
    public static IEnumerable<T> ForEachSelectDo<T>(this IEnumerable<T> source, Action<T> action)
    {
        return source.Select(x =>
        {
            action(x);
            return x;
        });
    }

Both functions have the same behaviour. The action is only executed, if the item is processed. If the retrieval of the items is stopped, the action is not executed.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!