convert generic type to base class - c#

public class Base {...}
public class Complex: Base {...}
public class MyOtherClass<T> {...}
and a list of the two type
List<MyOtherClass<Complex>> listOfComplex
List<MyOtherClass<Base>> listOfBase
I want have a list
listOfComplex.Union(listOfBase)
but i cant convert generic of a type to a generic of another type even if Complex derive fro Base
It's possibble to have a list of a base class?

Although Complex derives from Base, there is no such relationship between MyOtherClass<Complex> and MyOtherClass<Base>. That is the reason why you cannot create an Union of the two lists. For the framework both generic types are completely different.
The solution now really depends on what exactly your class does. Check out the topic of covariance and contravariance here in Docs - these are two special cases in which you will be permitted to create conversions between the two generic types if the types either are input or output only.
You could also add custom cast or manually cast the items to the "base" type before doing the "union" operation.

Because that's how Covariance and Contravariance in Generics works, specifically invariance, it means that you can use only the type originally specified; so an invariant generic type parameter is neither covariant nor contravariant.
You can't assign an instance of List<Base> to a variable of type List<Complex> or in the opposite direction. Same applies to custom generic classes. There is no implicit conversion that compiler could use in order to convert types.
Even if you add generic type constraint. A<B> and A<C> are two different types with no conversion between them even if C is inherited from B (because AC is not inherited from AB)

#MartinZikmund has explained why it does not work. The resolution to this kind of problem is to either derive the generic classes from a non-generic base class or to let them implement a common interface.
public class MyOtherClassBase { }
public class MyOtherClass<T> : MyOtherClassBase { }
Then you can specify MyOtherClassBase as generic parameter to Union explicitly. Now, Union will expect inputs of type IEnumerable<MyOtherClassBase>. Lists of type List<MyOtherClass<T>> are assignment compatible to IEnumerable<MyOtherClassBase>.
var result = listOfComplex.Union<MyOtherClassBase>(listOfBase);
Note the out keyword in the declaration
public interface IEnumerable<out T> : System.Collections.IEnumerable
It makes the interface covariant. See also SO question <out T> vs <T> in Generics and especially Reed Copsey's answer.

You should specify in your generic that T is a class base on Type :
public class MyOtherClass<T> where T : Base {...}
T will be accepted only if derive from Base And Union will should work
Here some documentation about Generics, thanks to #Marie:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters

Related

C# usage of where statement after function signature [duplicate]

I'm looking at the source code for the MvcContrib Grid and see the class declared as:
public class Grid<T> : IGrid<T> where T : class
What does the where T : class bit do?
It is a generic type constraint.
In this case it means that the generic type (T) must be a reference type, that is class, interface, delegate, or array type.
Other constraints are listed here.
You can also constrain the generic type to inherit from a specific type (base class or interface)
Another examples would be
public A<T> where T : AnInterface
where AnInterface is a interface class. It means then, that T must implement this interface.
These constraints are important, so that the compiler knows the operations which are valid for the type. For example you can not call functions of T without telling the compiler what functions the type provides.
From the Docs http://msdn.microsoft.com/en-us/library/d5x73970.aspx
where T : class
The type argument must be a reference type; this applies also to any class, interface, delegate, or array type.
It is a constraint on the type argument which says that T can either be a class or an interface but not an enum or a struct. So T must be a reference type and not a value type.
Best Regards,
Oliver Hanappi
It restricts T to be a reference type, including any class, interface, delegate, or array type.
It's a generic type constraint. It specifies that the type T has to be a reference type, i.e. a class and not a structure.
you can apply restrictions to the kinds of types that client code can use for type arguments when it instantiates your class are called as Constraints on Type Parameters
E.g : where T : class
Here where T is the Type , The type argument must be a reference type; this applies also to any class, interface, delegate, or array type.

Conversion of nested collections to interfaces

Why? Surely I do not need a user defined conversion as List(T) is an IList(T) and HashSet(T) is an IEnumerable(T). Thanks.
Cannot implicitly convert type 'System.Collections.Generic.List<System.Collections.Generic.HashSet<string>>' to 'System.Collections.Generic.IList<System.Collections.Generic.IEnumerable<string>>'. An explicit conversion exists (are you missing a cast?)
class Program {
static IList<IEnumerable<string>> GetSet() {
return new List<HashSet<string>>();
}
}
Because IList<T> is invariant.
To illustrate why this is a problem, consider the following example:
An IList<T> offers for instance a method to Add(T object) an IEnumerable<string>, which would conflict with your constructor expression new List<HashSet<string>>(). It would imply that I could call your program.GetSet() and add a new ArrayList<string>(), but your constructed instance will not allow it, since it has a contract that it only contains HashSet<string> instances (what would you return when one asks the content of the GetSet() containing an ArrayList<T>?
The fact that the typeparameter is doubly nested doesn't matter. For instance IList<Object> is not a superclass of IList<FooClass> either.
This is not the case with IEnumerable<T> itself (meaning that IEnumerable<T> is a IEnumerable<SuperT> as well), since the only functionality of an IEnumerable<T> is to output values. Which is allowed due to the Liskov Substitution Principle.
The principle says that when walking down in a class hierarchy, the
return types can only become more general (super classes/interfaces)
and the parameter types can only become more specific (sub
classes/interfaces).
C# offers tools to deal with this principle (called variance) by using the in and out keywords in the generic type declaration.
For instance if you are sure that a Foo<SubT> is a Foo<T> as well, you can define this as:
public class Foo<out T> {
T getResult () {
//do something
}
}
In that case T is covariant with respect to Foo. In case a type parameter is only used as input, it can be further specified. For instance Bar<T> is clearly a special case of Bar<SubT> in the following definition:
public class Bar<in T> {
void setParameter(T parameter) {
//do something
}
}
I think what I was really needing is a simple explanation of variance - contravariance and covariance - in the context of C# generics which I have found here and here:
The error message did not really lead me to that but now I will summarise:
Contravariance
A generic class, which of course is a template for a class rather than a class definition, can be made contravariant using the in keyword. A contravariant class allows assignments from a base class instance to a derived class instance ie BorderCollie = Dog
public interface AllowAssignmentsFromBaseToDerived<in T>
Covariance
A generic class can be made covariant using the out keyword. A covariant class allows assignments from a derived class instance to a base class instance ie Dog = BorderCollie
public interface AllowAssignmentsFromDerivedToBase<out T>
Variance is supported for array types since C# 1.0 and delegate types since C# 2.0, as well as generic type arguments since C# 4.0.
Would be nice to have had more responses to cover more points that I have missed, still feel a bit under informed.
More info lifted from here:
How can I create variant generic interfaces and delegates myself?
The out keyword marks a type parameter as covariant, and the in
keyword marks it as contravariant. The two most important rules to
remember:
You can mark a generic type parameter as covariant if it is used only
as a method return type and is not used as a type of formal method
parameters.
And vice versa, you can mark a type as contravariant if it
is used only as a type of formal method parameters and not used as a
method return type.
interface IVariant<out R, in A>
{
// These methods satisfy the rules.
R GetR();
void SetA(A sampleArg);
R GetRSetA(A sampleArg);
// And these don’t.
// A GetA();
// void SetR(R sampleArg);
// A GetASetR(R sampleArg);
}
Also, if you extend a variant generic interface it is invariant by default. You would need to specify In or Out as required.
Finally, as my explanation will be woefully inadequate try Eric Lippert's blog

C# using Generics both in the Interface and its implementor class

I want to create an interface that works for all the IComparable types. For example
public interface SortAlgorithm<T> where T : System.IComparable<T>
{
List<T> Sort(List<T> input);
}
And I want its implementors to be generic as well with the same specification I provide in the interface. Like the example below
public class InsertionSort<T> : SortAlgorithm<T>
Here is my purpose for doing this. I want all my sorting algorithms to work with all types that implements the IComparable interface. And want to provide the specification that the T is a subclass of IComparable once in the interface. But when I do this, I get the following error.
Error 1 The type 'T' cannot be used as type parameter 'T' in the generic type or method 'Algorithms.SortingAlgorithm.SortAlgorithm'. There is no boxing conversion or type parameter conversion from 'T' to 'System.IComparable'.
I did not understand the reason for this. I can implement a generic interface and make its implementor classes generic as well but I can't do the same when give a specification in the interface (where T : System.IComparable)
All derived generic classes will also have to implement the generic constraint.
Therefore, you should declare the class as:
public class InsertionSort<T> : SortAlgorithm<T> where T : System.IComparable<T>
What the error basically says is that the generic parameter T (which at this point can be any class or struct) is not guaranteed to implement IComparable<T>, as constrained by the base class (the SortAlgorithm interface).
You can provide this guarantee by specifying the constraint on the InsertionSort class as well, as presented above.

Why do generic type restrictions have to be redeclared on descendant types?

In C#, given a generic type such as this:
interface IGenericType<T> where T : new()
And a descendant type, such as:
class GenericTypeImplementation<U> : IGenericType<U>
Why do we need to explicitly restrict the generic type U with all the restrictions of the parent type?
class GenericTypeImplementation<U> : IGenericType<U> where U : new()
Am I right in inferring that the issue is in the compiler computing the union of restrictions?
interface IGenericType<T> where T : new()
interface IGenericType2<T> where T : SomeOtherType
class GenericTypeImplementation<U> : IGenericType<U>, IGenericType2<U>
/* Hypothesis: Compiler can't infer U must be "SomeOtherType + new()" */
In my opinion, the compiler could be smart enough to infer the restrictions theoretically. But it shouldn't be so smart, because a too-smart compiler is sometimes dangerous. Developers always need a clear/explicit definition of everything. See this scenario:
(1) there is an interface IFoo<T> where T : new()
(2) a class Foo<T> : IFoo<T> and the new() constraint is added automatically by the compiler(brilliant!)
(3) the class Foo<T> is a very base class in the whole project, class A<T> : Foo<T>, and then class B<T> : A<T>...
(4) Now another developer can hardly realize there is such a constraint by looking into the definition of the class, he will get weird compiling errors(well that's acceptable). But what if they are invoked by reflection? Sometimes the program is correct, because the data meets the restriction by accident.
The compiler is able to to infer that U must be convertible to SomeOtherType and must have a default constructor. It will generate a compiler error for each constraint:
Error 1 The type 'U' must have a public parameterless constructor in order to use it as parameter 'T' in the generic type or method '....IGenericType<T>'
Error 2 The type 'U' must be convertible to '....SomeOtherType' in order to use it as parameter 'T' in the generic type or method '....IGenericType2<T>'
This will also happen with just one of those interfaces implemented as well. The class must successfully implement both interfaces in order to be compiled:
class GenericTypeImplementation<U> : IGenericType<U>, IGenericType2<U>
where U : SomeOtherType, new()
{...}
or as a non-generic type:
class GenericTypeImplementation : IGenericType<SomeType>, IGenericType2<SomeOtherType>
{...}
To mark a class as implementing an interface is not a way of specifying constraints on the generic type parameters of a class; it is a way of requiring that those constraints exist on a new type parameter or that they be satisfied by a supplied type.
Perhaps you could think of it this way: an interface is a constrained set of classes and a generic class is also a constrained set of classes. A generic interface is a constrained set of generic classes. When you say that a generic class implements a generic interface, you are asking the compiler, "Is this generic class strictly within the set specified by this generic interface?" You are not merely intersecting them as a further constrained set of classes.
Because a generic type restriction is on the type parameter of the defining class (U in your example), from a CLR point of view, that is a different type from the type parameter of the interface.
The type parameter of the class need not be the actual type parameter of the interface. It need not even be a simple type, as in:
class Implementation<T> : IGenericType<List<T>> { /* ... */ }
In this case, the compiler recognizes that List<T> satisfies the constraint, and so no further specification is necessary. But without such knowledge about the generic type parameter, the compiler requires you to declare it explicitly.
It is instructive to compare this to the similar but not identical behaviour of generic methods. As with classes that implement interfaces, the type restrictions must be specified with the declaration. There is one notable exception: if the implementation is explicit. In fact, the compiler will generate an error when you try to re-impose the restrictions.
For example, given an interface
interface ISomething {
void DoIt<T>() where T : new();
}
the two correct ways to implement this interface are:
class OneThing : ISomething {
public void DoIt<T>() where T : new() { }
}
class OtherThing : ISomething {
void ISomething.DoIt<T>() { }
}
Leaving out the constraint in OneThing or iserting it in OtherThing produces a compile-time error. Why do we need the constraint in the first implementation and not in the second one? I'd say for the same reason I mentioned above for type constraints on interfaces: in the first implementation, the type T has no relation to the type parameter on the interface method, so it must be made explicit for the method to match the interface method. In the second implementation, the fact that we explicitly declare the interface means that the type parameter T is the exact same one that was used in the interface.

Generic interface in C#

I have a generic interface and I want to constrain types that this generic parameter can accept. Here is the interface:
public interface IBaseRequestRepository<T> where T : IRequestPackage,
IRequestDynamicPackage, IRequestHotelOnly, IRequestFlightOnly
{
IList GetByOccupancyAndTravelDate(int occupancyId, int travelBegYear,
int travelBegDate, int travelEndYear,
int travelEndDate);
}
But this gives an error:
Error 1 The type 'IRequestPackage' cannot be used as type parameter 'T' in the generic type or method 'IBaseRequestRepository'. There is no implicit reference conversion from 'IRequestPackage' to 'IRequestFlightOnly'.
Any suggestions?
You need to satisfy all generic constraints and not just one.
Thus you can't substitute IRequestPackage into T because it doesn't derive from all the other interfaces.
You can pass in either an interface type that inherits from all the interfaces you specified as a constraint or a class type that implements all these interfaces.
The error message suggests that IRequestPackage does not inherit from IRequestFlightOnly - does it?
Note that the where clause is an AND relationship, not an OR - so your where clause is that T must implement ALL of
IRequestPackage,
IRequestDynamicPackage
IRequestHotelOnly,
and IRequestFlightOnly
Looks like you want an IRequest interface or RequestBase abstract class ;)
You should use a type that implement all the interfaces specified by the constraint, not just one of them. It's an "AND", not an "OR"...
You should reconsider your type constraints and your program structure. It looks like you are missing a base interface or an abstract base class.

Categories