Is testing generic collections for referential equality in C# a silly idea?
Asked Answered
M

2

9

I'm implementing a special case of an immutable dictionary, which for convenience implements IEnumerable<KeyValuePair<Foo, Bar>>. Operations that would ordinarily modify the dictionary should instead return a new instance.

So far so good. But when I try to write a fluent-style unit test for the class, I find that neither of the two fluent assertion libraries I've tried (Should and Fluent Assertions) supports the NotBeSameAs() operation on objects that implement IEnumerable -- not unless you first cast them to Object.

When I first ran into this, with Should, I assumed that it was just a hole in the framework, but when I saw that Fluent Assertions had the same hole, it made my think that (since I'm a relative newcomer to C#) I might be missing something conceptual about C# collections -- the author of Should implied as much when I filed an issue.

Obviously there are other ways to test this -- cast to Object and use NotBeSameAs(), just use Object.ReferenceEquals, whatever -- but if there's a good reason not to, I'd like to know what that is.

Meander answered 13/3, 2013 at 14:53 Comment(5)
Have you looked at the MS immutable collections, btw? nuget.org/packages/Microsoft.Bcl.ImmutableGasbag
@JonSkeet Mmmm. What goodness you show us. Thanks.Cocksure
Any type is an Object, why would you need to cast?Pharr
@Pharr -- You need to cast it because the framework's implemented in terms of extension methods, and if you don't cast it, you get a more-specific extension method for IEnumerable, which unlike the less-specific one for Object, doesn't support the NotBeSameAs() operation.Meander
@JonSkeet Looks awesome, but the license seems to prohibit using it in production code? (Also, my original question still stands -- I'd love to use the immutable collections for the internal implementation, but I'd still like the more limited wrapper object around them to implement IEnumerable.)Meander
C
1

An IEnumerable<T> is not neccessarily a real object. IEnumerable<T> guarantees that you can enumerate through it's states. In simple cases you have a container class like a List<T> that is already materialized. Then you could compare both Lists' addresses. However, your IEnumerable<T> might also point to a sequence of commands, that will be executed once you enumerate. Basically a state machine:

public IEnumerable<int> GetInts()
{
    yield return 10;
    yield return 20;
    yield return 30;
}

If you save this in a variable, you don't have a comparable object (everything is an object, so you do... but it's not meaningful):

var x = GetInts();

Your comparison only works for materialized ( .ToList() or .ToArray() ) IEnumerables, because those state machines have been evaluated and their results been saved to a collection. So yes, the library actually makes sense, if you know you have materialized IEnumerables, you will need to make this knowledge public by casting them to Object and calling the desired function on this object "manually".

Calfskin answered 13/3, 2013 at 16:24 Comment(0)
H
1

In addition what Jon Skeet suggested take a look at this February 2013 MSDN article from Ted Neward:

.NET Collections, Part 2: Working with C5

Immutable (Guarded) Collections

With the rise of functional concepts and programming styles, a lot of emphasis has swung to immutable data and immutable objects, largely because immutable objects offer a lot of benefits vis-à-vis concurrency and parallel programming, but also because many developers find immutable objects easier to understand and reason about. Corollary to that concept, then, follows the concept of immutable collections—the idea that regardless of whether the objects inside the collection are immutable, the collection itself is fixed and unable to change (add or remove) the elements in the collection. (Note: You can see a preview of immutable collections released on NuGet in the MSDN Base Class Library (BCL) blog at bit.ly/12AXD78.)

It describes the use of an open source library of collection goodness called C5. Look at http://itu.dk/research/c5/

Helminthic answered 13/3, 2013 at 16:44 Comment(1)
I took a look at C5, but I was having a lot of weird casting issues trying to get it to interoperate with System.Collections.Generic. (At some point if I get frustrated enough with the out-of-the-box collections maybe I'll give it another try and turn those issues into StackOverflow questions....)Meander

© 2022 - 2024 — McMap. All rights reserved.