简体   繁体   中英

Why does Enumerable.Concat on same IEnumerable identify as a bug by SonarCube?

SonarQube identifies a bug in my code based on rule csharpsquid:S2114 and I cannot see how this applies to the code. The code in question concatenates the values IEnumerable from a dictionary Dictionary<string,object> attributes one time after another like so:

var valuesTwice = attributes.Values.Concat(attributes.Values).ToArray();

The rule states:

Collections should not be passed as arguments to their own methods

Passing a collection as an argument to the collection's own method is either an error - some other argument was intended - or simply nonsensical code.

Further, because some methods require that the argument remain unmodified during the execution, passing a collection to itself can result in an unexpected behavior.

However, as I understand it, this is not actually a method on the list attributes.Values itself, but an extension method, so the code in question could be written (less elegantly, in my opinion) like the variant added below (the original included for comparison):

var valuesTwice = attributes.Values.Concat(attributes.Values).ToArray();
var valuesTwice = Enumerable.Concat(attributes.Values, attributes.Values).ToArray();

From reading the documentation page on Concat , I don't see how the statement can have any unintended effects on the attributes Dictionary, which is what I understand the rule is to guard against. It is not like the Concat modifies the Values structure in the Dictionary.

My only explanation is that SonarQube's matching rules confuse this extension method as a method on the actual collection itself. Is this Concat safe to do, and I can just ignore this application of the rule, or am I missing something?

As a bonus question: Is there an alternative (elegant) way to achieve the same effect, producing an array where the Values are repeated?

Update/Clarification: My problem with the SonarQube description is the lack of distinction between passing a collection to a method on the collection itself (which may have unintended side effects that I think the rule is about) and passing a collection as both arguments to an extension method, see stackoverflow.com/questions/100196/net-listt-concat-vs-addrange. My reasoning is that the Concat method does not alter the collection, but returns a new collection (or rather, enumerable) combining the two passed collections. I question if Enumerable.Concat is an appropriate match for the rule, where I would agree that AddRange method is: feeding a collection "with its own tail" by passing it to AddRange, I would have a problem with, since it modifies the collection.

The reasoning behind the check in SonarQube can be found in their JIRA (and C# version, and JIC github PR). And it makes sense in quite a lot of scenarios.

As for bonus question, it is matter of preference and, maybe performance(if the code will be called a lot), but you can try something like:

var x = new[] {1,2};    
var times = 2;
x.SelectMany(i => Enumerable.Repeat(i, times)).ToList();

It will produce not the same output though, your code will return [1, 2, 1, 2] and this will - [1, 1, 2, 2] .

You can also create your own extension method:

    public static IEnumerable<T> RepeatElements<T>(this IEnumerable<T> en, int times)
    {
        // check arguments
        var inner = en is ICollection<T> col ? col : en.ToList();
        foreach (var elem in inner)
        {
            for (int i = 0; i < times; i++)
            {
                yield return elem;
            }
        }
    }

Reorder cycles if return order of elements is important.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM