I have created a method with two generic parameters where one parameter (itemsToAdd) must be the same type as the generic parameter of the next parameter (inputList).
See this demo code:
public class GenericsDemo
{
public void AddToList<TList, TItems>(TList inputList, params TItems[] itemsToAdd)
where TItems : IConvertible
where TList : IEnumerable<TItems>
{
IEnumerable<IConvertible> someOtherList;
// Sounds good, doesn't work..
//someOtherList = inputList;
// This works
someOtherList = (IEnumerable<IConvertible>)inputList;
}
}
I would expect the inputList can be directly assigned into the IEnumerable<IConvertible> someOtherList, but it needs a cast. Why the cast is needed?
Covariance only works for classes, not for structs (Source).
Thus, if you restrict TItems to reference types, your code compiles (fiddle):
where TItems : class, IConvertible
Related
I have the following 2 extension methods
namespace Services.Resources.Extensions
{
public static class DataMapExtensions
{
public static T ToDTO<T>(this BaseModel model)
{
return Mapper.Map<T>(model);
}
public static List<T> ToDTO<T>(this List<BaseModel> models)
{
return Mapper.Map<List<T>>(models);
}
}
}
The first method works perfectly fine.
//Note: FlightRoute inherits BaseModel
FlightRouteDTO foo = new FlightRoute().ToDTO<FlightRouteDTO>(); //This works!
However, the second method does not seem to work.
List<FlightRouteDTO> bar = new List<FlightRoute>().ToDTO<FlightRouteDTO>(); //This doesn't work!
The compiler is saying
Error CS1929 'List< FlightRoute>' does not contain a definition for 'ToDTO' and the best extension method overload 'DataMapExtensions.ToDTO< FlightRouteDTO>(List< BaseModel>)' requires a receiver of type 'List< BaseModel>'
But FlightRoute is of type BaseModel. If I change the type of bar to explicitly be List<BaseModel> ... then the problem goes away.
List<FlightRouteDTO> bar = new List<BaseModel>().ToDTO<FlightRouteDTO>(); //Why does it only work this way?
Am I missing something obvious?
That's just the expected behavior: you are trying to use a List<FlightRoute> as a List<BaseModel>, but just because FlitghtRoute inherits from BaseModel doesn't make List<FlitghtRoute> inherit from List<BaseModel>: they are completely different types.
What you could do, instead, is to leverage the use of Covariance, using interfaces instead of concrete types.
By changing your method signature to the following, you will notice that no compiler error will be generated:
public static List<T> ToDTO<T>(this IEnumerable<BaseModel> models)
{
return Mapper.Map<List<T>>(models);
}
That's because IEnumerable<T> is an interface with a covariant type parameter. By looking at the reference source, you will notice that this interface is declared with out T as generic type parameter, which indicates that T is covariant, and may be replaced by any inherited type when we use IEnumerable<T>.
You could introduce a second type parameter with a constraint:
public static List<T> ToDTO<T, K>(this List<K> models) where K : BaseModel
{
return Mapper.Map<List<T>>(models);
}
A better way to solve this might be to change signature like this:
public static List<T> ToDTO<T>(this IEnumerable<BaseModel> models)
{
return Mapper.Map<List<T>>(models);
}
You don't really need to accept a List, because you are not doing anything "list-specific" with the value, and AutoMapper understands any "collection" type you pass to it. IEnumerable<T> is enough, and because it's covariant with respect to T, it now works fine in your case:
// works
List<FlightRouteDTO> bar = new List<FlightRoute>().ToDTO<FlightRouteDTO>();
So I have a generic class, with Type as its generic parameter. In this class is a method, which has object parameter called value. Kinda like this:
public class Foo<Type> where Type : IComparable
{
public void Bar(object value)
{
DoSomething((Type)value);
}
}
As you can notice, I need to "DoSomething" with value (stored in object) that I first need to cast to Type. I even have my own overridden cast, which works on its own.
In this specific case, Type is generic, lets call it GenericType, and has this user-defined cast:
public static implicit operator GenericType<T>(T value)
{
return new GenericType<T>(value);
}
and value is an enum, lets say
public enum Number: short
{
Zero = 0,
One = 1,
Two = 2
}
The 'DoSomething((Type)value)' in this case is where Type is GenericType and value is Number.Zero. For some reason, this causes the cast to throw InvalidCastException: Specified cast is not valid. When I try it directly, i mean like..
GenericType<Number> z = (GenericType<Number>)Number.Zero;
..it works (I know, there is not explicit cast even needed). But for some reason, it does not work in the complex example I stated above. Can anyone help me understand and potentially fix that?
Why not just let your class use the generic type?
public class Foo<T> where T : IComparable
{
public void Bar(T value)
{
DoSomething(value);
}
}
No casting needed... and please don't use reserved words to name stuff.
A code sample:
interface IFoo { }
class FooImpl : IFoo { }
static void Bar<T>(IEnumerable<T> value)
where T : IFoo
{
}
static void Bar<T>(T source)
where T : IFoo
{
}
Can anybody explain, why this method call:
var value = new FooImpl[0];
Bar(value);
targets Bar<T>(T source) (and, hence, doesn't compile)?
Does compiler take into account type parameter constraints at all, when resolving overloads?
UPD.
To avoid confusion with arrays. This happens with any implementation of IEnumerable<T>, e.g.:
var value = new List<FooImpl>();
UPD 2.
#ken2k mentioned covariance.
But let's forget about FooImpl. This:
var value = new List<IFoo>();
Bar(value);
produces the same error.
I'm sure, that implicit conversion between List<IFoo> and IEnumerable<IFoo> exists, since I can easily write something like this:
static void SomeMethod(IEnumerable<IFoo> sequence) {}
and pass value into it:
SomeMethod(value);
Does compiler take into account type parameter constraints at all, when resolving overloads?
No, because generic constraints are not part of the function signature. You can verify this by adding a Bar overload that is identical except for the generic constraints:
interface IBar { }
static void Bar<T>(IEnumerable<T> value)
where T : IFoo
{
}
static void Bar<T>(T source)
where T : IBar
{
// fails to compile : Type ____ already defines a member called 'Bar' with the same parameter types
}
The reason your code doesn't compile is because the compiler chooses the "best" match based on the method signature, then tries to apply the generic constraints.
One possible reason why it doesn't is because this call would be ambiguous:
{suppose List<T> had an Add<T>(IEnumerable<T> source) method}
List<object> junk = new List<object>();
junk.Add(1); // OK
junk.Add("xyzzy") // OK
junk.Add(new [] {1, 2, 3, 4}); //ambiguous - do you intend to add the _array_ or the _contents_ of the array?
The obvious fix is to use a different name for the Bar method that takes a collection (as is done in the BCL with Add and AddRange.
EDIT: Ok, the reason why Bar<T>(T source) is selected over Bar<T>(IEnumerable<T> source) when passing an List is because of the "7.5.3.2 Better function member" section of the C# language reference. What it says is that when an overload resolution must occur, the arguments types are matched to the parameters types of the applicable function members (section 7.5.3.1) and the better function member is selected by the following set of rules:
• for each argument, the implicit conversion from EX to QX is not better than the implicit conversion from EX to PX, and
• for at least one argument, the conversion from EX to PX is better than the conversion from EX to QX.
(PX being the parameter types of the first method, QX of the second one)
This rule is applied "after expansion and type argument substitution". Since type argument substitution will swap the Bar(T source) to Bar>(IList source), this method arguments will be a better match than the Bar(IEnumerable source) which needs a conversion.
I couldn't find a online version of the language reference, but you can read it here
EDIT: misunderstood the question initially, working on finding the correct answer in the c# language spec. Basically IIRC the method is selected by considering the most appropriate type, and if you don't cast your parameter to IEnumerable<> exactly, then the Bar<T>(T source) will match the parameter type exactly, just like in this sample:
public interface ITest { }
public class Test : ITest { }
private static void Main(string[] args)
{
test(new Test() ); // outputs "anything" because Test is matched to any type T before ITest
Console.ReadLine();
}
public static void test<T>(T anything)
{
Console.WriteLine("anything");
}
public static void test(ITest it)
{
Console.WriteLine("it");
}
Will link to it when found
Because the cast between an array and an enumerable must be explicit: this compiles
var value = new FooImpl[0].AsEnumerable();
Bar(value);
and so does this:
var value = new FooImpl[0] as IEnumerable<IFoo>;
Bar(value);
From the doc:
Starting with the .NET Framework 2.0, the Array class implements the
System.Collections.Generic.IList,
System.Collections.Generic.ICollection, and
System.Collections.Generic.IEnumerable generic interfaces. The
implementations are provided to arrays at run time, and as a result,
the generic interfaces do not appear in the declaration syntax for the
Array class.
So your compiler doesn't know that the array matches the signature for Bar, and you have to explicitly cast it
As of c# 7.3, generic constraints are now considered part of the method signature for the purpose of overload resolution. From What's new in C# 7.0 through C# 7.3: Improved overload candidates:
When a method group contains some generic methods whose type arguments do not satisfy their constraints, these members are removed from the candidate set.
Thus in c# 7.3 / .Net Core 2.x and later, the code shown in the question compiles and runs successfully, and Bar<T>(IEnumerable<T> value) is called as desired.
A demo .Net 5 fiddle here now compiles successfully
A demo .NET Framework 4.7.3 fiddle here still fails to compile with the error:
The type 'FooImpl[]' cannot be used as type parameter 'T' in the generic type or method 'TestClass.Bar<T>(T)'. There is no implicit reference conversion from 'FooImpl[]' to 'IFoo'.
That's a covariance issue. List<T> is not covariant, so there is no implicit conversion between List<FooImpl> and List<IFoo>.
On the other hand, starting from C# 4, IEnumerable<T> now supports covariance, so this works:
var value = Enumerable.Empty<FooImpl>();
Bar(value);
var value = new List<FooImpl>().AsEnumerable();
Bar(value);
var value = new List<FooImpl>();
Bar((IEnumerable<IFoo>)value);
I have a few questions about generic classes with Enums.
First of all, I declared my class like this:
public class MyClass<TEnum> where TEnum : struct, IConvertible
But, I'm getting an error that states that my class cannot be used with type arguments.
Moreover, I need to convert the Enum's value to an Integer. How can I do that?
public void SomeMethod(TEnum value)
{
int a = (int)value; // Doesn't work, need to cast to Enum first (?).
}
Thanks.
You already have what you need since you declared requirement IConvertible. Just use ToInt32 etc methods:
public class MyClass<TEnum> where TEnum: struct, IConvertible
{
public int SomeMethod(TEnum value)
{
return value.ToInt32(null);
}
}
For example .NET type decimal is a struct and an IConvertble:
MyClass<decimal> test = new MyClass<decimal>();
Console.WriteLine(test.SomeMethod(150m));
For other classes be sure that you implement IConvertible.
You have declared your generic type parameter to implement IConvertible and that interface has a ToInt32 method.
I have the following code:
public interface IDrilldown
{
void AddCriteria<T>(T Criterion);
}
public class MyClass<W> : IDrilldown // where W : class
{
void IDrilldown.AddCriteria<T>(T Criterion)
{
W value = Criterion as W;
...
}
}
Unfortunately, the cast I have above will not work unless W has the constaint in the code. I would like to have this using value types. Is it at all possible?
I cannot make W and T the same type. My interface does not have a type associated with it globally, only the internal data types.
This is so that I can have a List all having different T's
I was able to find a way to do it, it's a little hacky but allows it to work:
class MyClass<W> : IDrilldown {
void IDrilldown.AddCriteria<T>(T Criterion) {
if (Criterion is W) {
W value = (W)Convert.ChangeType(Criterion, typeof(W));
// value is W, have fun
// or - as Snowbear pointed out in the comments
W value = (W)(object)Criterion;
// works just as well....
} else {
// value is NOT W and could not be converted.
}
}
}
The only drawback with this is, Convert.ChangeType will use converters to change between internal objects, so string value = (string)Convert.ChangeType(1, typeof(string)) will work and return "1" instead of throwing an exception.
To clarify on how this works, the documentation states:
For the conversion to succeed, value must implement the IConvertible interface, because the method simply wraps a call to an appropriate IConvertible method. The method requires that conversion of value to conversionType be supported.
so for this method to work with custom types you will need to implement the IConvertible interface to convert from one custom type to any other type. In the code sample above, if both T and W are the same type, the Convert.ChangeType will succeed, even if the custom object does not implement IConvertiable.
Would the dynamic keyword help you out?
Something like this:
public interface IDrilldown
{
void AddCriteria<T>(T Criterion);
}
public class MyClass : IDrilldown
{
void IDrilldown.AddCriteria<T>(T criterion)
{
dynamic value = criterion;
// can use typeof() to figure out type if needed...
...
}
}