1

I'm using a factory to create IComparer<User> objects to sort a list of users.

I have 2 classes : Ascending and Descending, both implement IComparer<User>. Here's the code :

namespace Test { public class Program { public static void Main(string[] args) { List<User> users = new List<User>(); users.Add(new User("foo", "bar")); // ... IComparer<User> cmp = ComparerFactory.GetComparer("FirstName", true); if (cmp != null) { users.Sort(cmp); } } } public class User { public string FirstName { get; set; } public string LastName { get; set; } public User(string firstName, string lastName) { FirstName = firstName; LastName = lastName; } } public class UserFirstNameComparer { public class Ascending : IComparer<User> { public int Compare(User u1, User u2) { return String.Compare(u1.FirstName, u2.FirstName, true); } } public class Descending : IComparer<User> { public int Compare(User u1, User u2) { return new UserFirstNameComparer.Ascending().Compare(u1, u2) * -1; } } } public static class ComparerFactory { public static IComparer<User> GetComparer(string fieldName, bool ascending) { switch (fieldName) { case "FirstName": return ascending ? new UserFirstNameComparer.Ascending() : // ERROR IS HERE new UserFirstNameComparer.Descending(); //... } return null; } } 

But I get an error (line : new UserFirstNameComparer.Ascending() :) :

Type of conditional expression cannot be determined because there is no implicit conversion between 'Test.UserFirstNameComparer.Ascending' and 'Test.UserFirstNameComparer.Descending'

I dont understand what it means, both are IComparer objects, so what's the problem ? The weird thing is that I can fix the error with a (unnecessary ?) cast :

// This works return ascending ? (IComparer<User>) new UserFirstNameComparer.Ascending() : new UserFirstNameComparer.Descending(); // This works too return ascending ? new UserFirstNameComparer.Ascending() : (IComparer<User>) new UserFirstNameComparer.Descending(); 

Of course it works when I cast in both cases. But I do not understand why it works with only one cast, and why it does not when there is no cast. Any ideas ?

(I'm using VS 2012, .NET v4.0.30319)

3
  • 1
    See the answer to this question: stackoverflow.com/questions/202271/… Commented Jul 9, 2014 at 11:58
  • Instead of having two Comparers (which is very silly), why don't you just create one regular Comparer (ascending) and do list.Sort(cmp).Reverse() or list.OrderBy(cmp) / list.OrderByDescending(cmp)? Commented Jul 9, 2014 at 12:01
  • 1
    Note that multiplying by -1 as you do above, will not work as desired if the return value of the Compare call is -2147483648. Some Compare implementations do return other values than just -1, 0 and +1, and are allowed to do so. Commented Jul 9, 2014 at 12:17

2 Answers 2

4

The expression

cond ? X : Y 

just requires (simplified) that either the compile-time type of X is implicitly convertible to the compile-time type of Y, or vice versa. It is not going to search through all interfaces and base classes of the two types to try and find some "common" type of them. (Even if it did, how would it handle the case with multiple common interfaces?)

This is how the language is designed. It is not relevant what the return type of your method is, that cannot be taken into consideration when trying to resolve a ?: expression.

You found the solution already, to cast X and/or Y to the desired type, explicitly.


Having answered your question, I also have a suggestion on how you can do this without writing your two classes Ascending and Descending. You can create an IComparer<> like this:

return ascending ? Comparer<User>.Create((u1, u2) => String.Compare(u1.FirstName, u2.FirstName, true)) : Comparer<User>.Create((u1, u2) => String.Compare(u2.FirstName, u1.FirstName, true)) ; 

so no need (possibly) for those two classes of yours.

Sign up to request clarification or add additional context in comments.

1 Comment

I should mention that the Comparer<>.Create method was new in .NET 4.5 (2012). Delegates of type Comparison<> have existed since .NET 2.0 (2005).
4

new UserFirstNameComparer.Ascending() returns an instance of Ascending. new UserFirstNameComparer.Descending() returns an instance of Descending.

Even though both implement IComparer, the type of these instances is different. When you cast one of them to IComparer, the resulting object has type IComparer. Now it is possible to do an implicit conversion, since one is an interface and the other a class implementing it.

To quote the documentation, for the conditional expression

condition ?: first_expression : second_expression 

Either the type of first_expression and second_expression must be the same, or an implicit conversion must exist from one type to the other.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.