C# overload resolution and generics - c#

I have the following methods
public static EnumerableAssertions<T> AssertThat<T>(IEnumerable<T> collection)
{
Debug.WriteLine("Enumerable!");
return new EnumerableAssertions<T>(collection);
}
public static ObjectAssertions<T> AssertThat<T>(T value) where T : class
{
Debug.WriteLine("Generic fallback!");
return new ObjectAssertions<T>(value);
}
But why does the following call resolve to the generic fallback?
List<object> list = null;
AssertThat(list);
From my understanding the overload for IEnumerable<T> should be more specific than the generic T : class but C# seems to see that differently. If I add an overload for the exact type List<T> it works just fine, but of course I don't want to add specific overloads for every type inheriting IEnumerable<T>

OP:
From my understanding the overload for IEnumerable should be more specific than the generic T : class but C# seems to see that differently
Apples and oranges.
This method:
public static ObjectAssertions<T> AssertThat<T>(T value) where T : class
...contains the explicit constraint:
where T : class
...whilst:
public static EnumerableAssertions<T> AssertThat<T>(IEnumerable<T> collection)
...has no explicit constraints at all.
OP:
But why does the following call resolve to the generic fallback?
The code:
List<object> list = null;
AssertThat(list);
...is most likely resolving to the generic one (the one with the where T : class) because List<> is a reference type and is more in line with where T : class than an unrestricted IEnumerable<T>.

Related

"where T : class" VS regular class in an extention method

I would like a technical difference between the following two Extension method declarations and when you would use one over the other:
public static void DoSomething(this MyClass theObject)
vs
public static void DoSomething<T>(this T theObject) where T : MyClass
For example, I know there is a difference when using this with an object of a class that inherits from MyClass class but I don't know why.
Assume this implementation:
public static List<T> DoSomething<T>(this T theObject) where T : MyClass
=> new List<T>();
Invoked as
MyChildClass x; // MyChildClass : MyClass
var list = DoSomething(x);
// list is an instance of List<MyChildClass>, instead of List<MyClass>
Use first (non-generic) when you don't need to know the "actual"
(child) type.
Use second (generic) when you plan to use the "actual" type in
further generic code.
I see it as a "smell" when someone designs a class/function as generic, even though it "doesn't need to be" generic = the base type would suffice. (Equivalent 'generic' impl is somewhat more difficult to read)

Using generics in interfaces in C#

I have an app written in C#. My app has a class that looks like the following:
public class FinderA
{
public IEnumerable<FinderA> GetItems()
{
return FinderA.FindAll();
}
}
I want to require other classes to do something similar. I cannot use a base class because my actual implementation is already using a base class. For that reason, I want to create an interface. Currently, I'm trying the following:
public interface IFinder
{
IEnumerable<T> GetItems();
}
When I use this approach, I get a compile-time error that says: "The type or namespace name 'T' could not be found (are you missing...". To overcome this, I add <T> to the end of the interface name so it looks like this:
public interface IFinder<T>
{
IEnumerable<T> GetItems();
}
This is turn generates another compile-time error that says: "Using the generic type 'IFinder' requires 1 type arguments.". My challenge is, I want the interface to be generic. I do not want to pass in a type. Is there a way around this? If so, what/how?
There is no way around this; you'll need to actually supply the generic type argument when declaring that a class implements the interface.
You can do this at the method level instead of as a generic type on the interface itself.
public interface IFinder
{
IEnumerable<T> GetItems<T>();
}
Your code can then call it like such:
IFinder finder = // Create finder instance
IEnumerable<MyClass> discoveredClasses = finder.GetItems<MyClass>();
If you want to ensure that MyClass is a class that implements IFinder, you can constrain the method.
public interface IFinder
{
IEnumerable<T> GetItems<T>() where T : IFinder;
}
That will cause the following to generate a compiler error:
public class Foo
{
}
public class Bar
{
Bar()
{
IFinder finder = // Create finder.
// This fails because <T> (Foo) does not implement IFinder.
IEnumerable<Foo> fooItems = finder.GetItems<Foo>();
}
}
but it will allow the following to compile
public class MyFinderA : IFinder
{
IEnumerable<T> GetItems<T>() where T : IFinder
{
return // Create collection of T
}
public class Bar
{
Bar()
{
IFinder finder = // Create finder.
// This works as <T> (MyFinderA) is an IFinder implementation
IEnumerable<MyFinderA> finderItems = finder.GetItems<MyFinderA>();
}
}
If you want your interface to be generic but you are not willing to supply a type argument, you can return an IEnumerable<object>.
public interface IFinder {
IEnumerable<object> GetItems();
}
All class types extend from System.Object so that should suffice for any type in your applicacion (enums and structs would get boxed)
Please note that this approach requires the interface's consumer to cast the generic object instances to the appropiate types.
The alternate (and recommended) approach is to make the interface use type parameters, but for that you must supply the appropiate type arguments

C# Generics not working as expected

We have a problem with the usage of generics.
We have a generic collection of generic keyvalue pair which is defined as follows
public class KeyValueTemplate<K, V> : IGetIdentifier<K>
{
//...
}
public class KeyValueListTemplate<K, V> : ObservableCollection<KeyValueTemplate<K, V>>
{
//....
}
public class KeyValueStringListTemplate : KeyValueListTemplate<string,string> { }
We are using this in the code as follows
public class test
{
public KeyValueStringListTemplate SetValuesList{get;set;}
public ObservableCollection<IGetIdentifier<string>> GetList()
{
return SetValuesList;
}
}
The complier is not accepting this. The error is
Cannot convert type 'KeyValueStringListTemplate' to 'System.Collections.ObjectModel.ObservableCollection<IGetIdentifier<string>>
Why?? Both the types are same to me.
This line
public class KeyValueListTemplate<K, V> : ObservableCollection<KeyValueTemplate<K, V>>
defines a new type, KeyValueListTemplate, that is a subtype of ObservableCollection, so they are different types. KeyValueListTemplatecan be safely converted to ObservableCollection, because it has a superset of ObservableCollection's functionality (by Liskov Substitution Principle), but the opposite conversion is not safe.
Its a matter of covariance in generics, which was not exposed to c#/vb.net before .net 4.
While it seem trivial that you can do this:
IEnumerable<string> strings = new List<string>();
// An object that is instantiated with a more derived type argument
// is assigned to an object instantiated with a less derived type argument.
// Assignment compatibility is preserved.
IEnumerable<object> objects = strings;
which is what your code is doing at the bottom line, it wasnt supported up to .net 4
The article i linked to explanis how to implement it and how it works.

C# generic method rejecting a type even though it implements required interface

Hopefully I've described it correctly. I have a 'generic method' which looks like the below. It accepts a list of any Icomparable/Iequatable type and returns a class 'compareResult' shown below containing lists of matched/unmatched items.
public partial class Comparers
{
public class compareResult<T>
{
public List<T> unchangedItems;
public List<T> changedItems;
public List<T> leftOrphans;
public List<T> rightOrphans;
}
public static compareResult<T> stepCompare<T>(List<T> leftList, List<T> rightList, bool confirmUniqueIDs = true) where T : IEquatable<T>, IComparable
{
...
I now try to pass in a list of 'LicencedCustomer' which is defined as below, and implements the CompareTo and Equals methods to implement the IComparable/IEquatable interfaces.
public class LicencedCustomer : IEquatable<LicencedCustomer>, IComparable<LicencedCustomer>
{
public string LMAA_CODE {get; set;}
...
Now I try to pass two lists of customers per below:
Comparers.compareResult<LicencedCustomer> result = new Comparers.compareResult<LicencedCustomer>();
result = Comparers.stepCompare(leftList, rightList);
But it says "Error 1 The type 'MFTests.LicencedCustomer' cannot be used as type parameter 'T' in the generic type or method 'MF.Comparers.stepCompare(System.Collections.Generic.List, System.Collections.Generic.List, bool)'. There is no implicit reference conversion from 'MFTests.LicencedCustomer' to 'System.IComparable'...
I thought I had implemented IComparable though it refers to conversion which I don't really understand. Sorry for the long explanation, I tried to keep it as brief as possible.
Any thoughts on what I'm doing wrong?
The generic method does not include the generic type identifier, T.
where T : IEquatable<T>, IComparable
should be
where T : IEquatable<T>, IComparable<T>

Generics Casting

interface Base { ... }
class Sub : Base { ... }
class OtherBase<T> where T : Base { ... }
class OtherSub<T> : OtherBase<T> where T : Base { ... }
//...in some class
void Call<T>() where T : OtherBase<Base> { }
//...
Call<OtherSub<Sub>>(); //compile fails...
Seems like when using generics, the compiler won't cast a inner generic type (Base/Sub) in the
generic type (OtherBase/OtherSub). Why does this happen?
Update:
Please also explain the difference between the above and the following (which works)
void Call<T>() where T : Base { }
//...
Call<Sub>();
Forbidding this behaviour (known as “generic variance”) is necessary because otherwise the following code would compile:
List<string> strlist = new List<string>();
List<object> objlist = strlist;
objlist.Add(42);
We’ve added a number to a list of strings. Not good. (Incidentally, the code would compile for arrays instead of Lists because Java allowed this for some reason; however, this will raise a runtime exception.)
You can avoid this in your case though:
static void Call<U, T>(T x) where U : Base where T : OtherBase<U> { }
And call it like this:
Call(new OtherSub<Sub());
C# 4.0 furthermore provides generic variance for interfaces. However, their use isn’t often necessary.
Your issue is linked to a concept called variance/covariance. In fact, if A inherits from B, Class<A> isn't a Class<B>.
See this example:
Class<T> exposes a public method foo(T param)
If Class<A> was a Class<B>, then a method having a reference to Class<B> as a Class<A> and calling foo(B param) (with a B instance) would be calling foo(A param). And B isn't a A.
In fact, Class<A> can inherit from Class<B> only if T is used as a return value only in Class<T>.
This is enforced in .NET 4 through the out keyword for generics interface. Class<T> could therefore implement IClass<out T>.
Konrad has a good advice on how to fix your code. If you wanted to use C# 4's variance, you could do it like this:
interface IOtherBase<out T> where T : Base { }
class OtherBase<T> : IOtherBase<T> where T : Base { }
class OtherSub<T> : OtherBase<T> where T : Base { }
static void Call<T>() where T : IOtherBase<Base> { }
Call<OtherSub<Sub>>() would work then.

Categories

Resources