views:

83

answers:

1

Initialization of list with lambdas causes high IL cyclomatic complexity: why, and how remove this complexity? For example following code causes the static constructor of the class (which is actually compiler generated) to be very complex: 1 + the list count.

static List<Predicate<string>> list = new List<Predicate<string>>()
{
    s => s == null,
    s=> s.StartsWith(“R”),
    ... With a lot of predicates like that ….
};

Note: complexity is computed with NDepend

+1  A: 

Why? Because ILCC is defined as the number of different jump/branch destinations. That list you are initializing contains a lot of if/then logic, contained within the lambdas. I presume the language-dependent CC is lower?

High cyclomatic complexity is just a hint that your functions are over-complex, and thus hard to understand and maintain and test. Whether that hint is correct in this case would depend on how you use that list of predicates. But it is just that, a hint. Keeping CC low should not be regarded as a law of nature. If you think the code is maintainable and testable, note the high ILCC in your documentation, explain why it doesn't matter, and move on.

Pontus Gagge
Actually if I decompile the code and if I understand well IL (I'm a dummy about it), the lambdas seem to be generated separatly. The branches look more like an artefact of compiler code generation.
sthiers
Perhaps you could add a brief extract of the decompiled IL, for us who are too lazy to run to VS. I'm surprised if the lambdas aren't actually generated at the point of static initialization to count towards the static constructor complexity.
Pontus Gagge
L_0008: ldsfld class Predicate`1<string> Foo.Class1::CS$<>9__CachedAnonymousMethodDelegate12L_000d: brtrue.s L_0022L_000f: ldnull L_0010: ldftn bool Foo.Class1::<FooFunction>b__0(string)L_0016: newobj instance void [mscorlib]Predicate`1<string>::.ctor(object, native int)L_001b: stsfld class [mscorlib]Predicate`1<string> Foo.Class1::CS$<>9__CachedAnonymousMethodDelegate12L_0020: br.s L_0022L_0022: ldsfld class Predicate`1<string> Foo.Class1::CS$<>9__CachedAnonymousMethodDelegate12L_0027: callvirt instance void Generic.List`1<class [mscorlib]Predicate`1<string>>::Add(!0)
sthiers
It's hard to tell for certain without fuller docs from NDepend (or their source code!), but it seems fairly obvious that they count the IL contents of anonymous delegates (whether from lambdas or written otherwise) against the complexity of their referencing method for ILCC. Which seems natural: after all, you can hide a lot of conditional clauses and alternate code execution paths inside lambdas. After all, there are plenty of ways of using lambdas to **complicate** code!
Pontus Gagge