12

I have two extension methods:

public static IPropertyAssertions<T> ShouldHave<T>(this T subject)
{
    return new PropertyAssertions<T>(subject);
}

public static IPropertyAssertions<T> ShouldHave<T>(this IEnumerable<T> subject)
{
    return new CollectionPropertyAssertions<T>(subject);
}

Now I write some code which uses it:

List<Customer> collection2 = new List<Customer>(); 
collection2.ShouldHave(); //first overload is chosen
IEnumerable<Customer> collection3 = new List<Customer>(); 
collection3.ShouldHave(); //second overload is chosen

Second overload is chosen only if I explicitly specify IEnumerable type. Is there any way to make second overload to be chosen in both cases?

Dennis Doomen
  • 8,368
  • 1
  • 32
  • 44
SiberianGuy
  • 24,674
  • 56
  • 152
  • 266
  • There will be no difference between `collection1` and `collection2`. They are exactly the same code, only written differently. – svick Mar 25 '12 at 13:37
  • @svick, yes, but I wanted to show all syntax options – SiberianGuy Mar 25 '12 at 13:49
  • 3
    Note that the fact it's an extension method is irrelevant; overload resolution handles it like a normal static method. – Thomas Levesque Mar 25 '12 at 14:09
  • 2
    Sure, delete the first one. Extension methods that can work on *any* object without any constraint are best avoided, way too much noise. Also notable is how unreadable the source code becomes. ShouldHave *what*? Just don't use an extension method at all, make it a regular public static method. – Hans Passant Mar 25 '12 at 14:16
  • @HansPassant, it is actually fluent syntax, I just didn't show the rest calls like collection.ShouldHave().AllProperties().EqualTo(...) – SiberianGuy Mar 25 '12 at 14:25

4 Answers4

7

The first overload is a better match, because T is inferred as List<Customer>, which gives an exact match. For the second overload, it would infer T as Customer, so the parameter would be IEnumerable<Customer>, which is a less exact match than List<Customer>.

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
5

Don't think so. Don't think it's possible that in this case IEnumerable<T> overload will be always called, as it has less accurate match in regard of T type. If you don't specify a concrete IEnumerable<T>, the best match will be always a first method, in this case.

Tigran
  • 61,654
  • 8
  • 86
  • 123
1

In Fluent Assertion 2.0 I've finally managed to solve the above problems in a decent way. Read all about that here: http://www.dennisdoomen.net/2012/09/asserting-object-graph-equivalence.html

Dennis Doomen
  • 8,368
  • 1
  • 32
  • 44
1

ShouldHave() has a double meaning. In the first case, ShouldHave() has a return about the object supplied by the subject-parameter. In the second case, the return is about items in the enumeration, and not about the enumeration itself.

In case I create my own collection and I want to test this collection itself (not the items), I certainly want ShouldHave(this T subject) to be called and not ShouldHave(this IEnumerable subject).

Maybe you should reconsider your design. The second ShouldHave() does two things, so should be split into a method that extracts the collection-items and a call to the first ShouldHave(), that you already have.

  • That's exactly what we're trying to do, but we have some troubles with the overload resolution of the C# compiler. – Dennis Doomen Mar 25 '12 at 16:37
  • The C# compiler is correct in that it prefers the perfect match over a match that needs a typecast first. I think your design is imperfect and you want it solved by the C# compiler being imperfect as well. What if I indeed want to apply ShouldHave(this T subject) to my collection? Why do you want to block this scenario? – Rick Beerendonk Mar 25 '12 at 16:45
  • But to remain serious, you have a point. But the entire purpose of extension methods is to extend existing types without touching the original code. So my typical approach is to come up with a nice-looking fluent syntax and then to try implement it. Unfortunately, this particular situation is not easily resolvable. – Dennis Doomen Mar 25 '12 at 17:05
  • I expect x.ShouldHave() to work with x and not with any property of x or items of x in case x is an IEnumerable. I would prefer x.Enumeration().ShouldHave() in case it is an enumeration. You like to make that easier by moving the two operations into one. If you really want, you could use reflection in the first method to see if subject (= T) implements IEnumerable (where T is an other T, confusing) and call the second method. I don't like that. Now ShouldHave has 2 options and will behave different than normal (and I cannot see this just by looking at the interface of the method). – Rick Beerendonk Mar 25 '12 at 17:55