Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Yield Return in C# - makes fail tests for publicity of fields

Today I encoutered problem connected with C#. I have to write an program which will pass some tests. Everything I implemented is working correctly, but there is one test which disallows using public fields, and it fails.

I have a generic class implementing IEnumerable, and the thing that causes problem is:

public IEnumerator<R> GetEnumerator()
{
    foreach (R r in roomList)
        yield return r;
}

If i put into this class "return null", test passes fine. I wonder what can be wrong? Here is a test report:

Result Message:       Assert.Fail failed. Detected type "Exercise.Hotel`2+<GetEnumerator>d__0" with public filed(s) "Exercise.Hotel`2[R,SC] <>4__this, R <r>5__1, Enumerator <>7__wrap2", which is not allowed.

Here is also code of test. To be honest i am not very familiar with how it works. Of course it can be also caused by bad test.

        var withPublicField = types.Where(t => !t.IsEnum)
            .Where(t => t.GetFields(BindingFlags.Public | BindingFlags.Instance).Count() > 0)
            .Where(t => !t.Name.StartsWith("<>c__DisplayClass"))
            .ToDictionary(t => t.FullName, t => t.GetFields(BindingFlags.Public | BindingFlags.Instance));

Thanks for help. It's my first question so I hope that i did everything well :)

like image 261
Bóg Programowania Avatar asked Jan 10 '23 11:01

Bóg Programowania


1 Answers

It's an odd test you are talking about.

The reason the iterator method (i.e. a method with yield return in it) triggers the failure is that the C# compiler rewrites such methods to return an instance of a hidden class that implements the method as a state machine. It's that type that has the disallowed public fields in it.

Personally, it's my opinion that failure is a false positive. While I agree that public fields in general are an abomination, in this case you're dealing with hidden, compiler-generated code. This particular scenario should be fine.

Hopefully you can just suppress the failure in this case, with an explanation of "it's not my fault!" The alternative is to implement your own class that implements the IEnumerator<T> type for the scenario, and which of course does not contain public fields.

EDIT: I notice looking at the test code that it already attempts to exclude compiler-generate code for captured variables (i.e. Name.StartsWith("<>c__DisplayClass")). So perhaps the test author would add a similar exclusion for the compiler-generated <GetEnumerator> types (or of course check for [CompilerGenerated] per Lee's answer).

like image 59
Peter Duniho Avatar answered Jan 31 '23 19:01

Peter Duniho