Extension methods overload choice
Asked Answered
W

4

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?

Wireless answered 25/3, 2012 at 12:13 Comment(5)
There will be no difference between collection1 and collection2. They are exactly the same code, only written differently.Kinsler
@svick, yes, but I wanted to show all syntax optionsWireless
Note that the fact it's an extension method is irrelevant; overload resolution handles it like a normal static method.Laise
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.German
@HansPassant, it is actually fluent syntax, I just didn't show the rest calls like collection.ShouldHave().AllProperties().EqualTo(...)Wireless
J
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

Jacobba answered 5/9, 2012 at 18:33 Comment(0)
L
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>.

Laise answered 25/3, 2012 at 14:15 Comment(0)
S
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.

Syreetasyria answered 25/3, 2012 at 12:43 Comment(0)
A
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.

Aleksandropol answered 25/3, 2012 at 16:12 Comment(4)
That's exactly what we're trying to do, but we have some troubles with the overload resolution of the C# compiler.Jacobba
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?Aleksandropol
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.Jacobba
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<T> (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).Aleksandropol
J
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

Jacobba answered 5/9, 2012 at 18:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.