Generic constraint ignores co-variance - c#

Let's say we have an interface like
public interface IEnumerable<out T>
{ /*...*/ }
that is co-variant in T.
Then we have another interface and a class implementing it:
public interface ISomeInterface {}
public class SomeClass : ISomeInterface
{}
Now the co-variance allows us to do the following
IEnumerable<ISomeInterface> e = Enumerable.Empty<SomeClass>();
So a IEnumerable<SomeClass> is assignable to a variable (or method parameter) of type IEnumerable<ISomeInterface>.
But if we try this in a generic method:
public void GenericMethod<T>(IEnumerable<T> p) where T : ISomeInterface
{
IEnumerable<ISomeInterface> e = p;
// or
TestMethod(p);
}
public void TestMethod(IEnumerable<ISomeInterface> x) {}
we get the compiler error CS0266 telling us that an IEnumerable<T> cannot be converted to an IEnumerable<ISomeInterface>.
The constraint clearly states the T is derived from ISomeInterface, and since IEnumerable<T> is co-variant in T, this assignment should work (as shown above).
Is there any technical reason why this cannot work in a generic method? Or anything I missed that makes it too expensive for the compiler to figure it out?

Change your GenericMethod and add generic constraint class:
public void GenericMethod<T>(IEnumerable<T> p) where T : class, ISomeInterface
{
IEnumerable<ISomeInterface> e = p;
// or
TestMethod(p);
}
Covariance does not support structs, so we need to tell that we want to use classes only.

Related

.NET Core Cast to interface with generic type parameter of type which implements interface

In .NET Core C#
I' trying something like this:
(IInterface<IParameter>)instance
Where instance is new Implementation<Parameter>()
And Implementation : IInterface & Parameter : IParameter
The issue is with the casting of the generic parameter. When I provide Parameter instead of IParameter it works but at compile time there is no way to know which type that implements IParameter will be used. All of these objects will be created via reflection.
So is there any way this cast works? Or some other way to implement this like providing no generic type parameter like you can with typeof.
EDIT Thanks to Ziriax
A Fully Working Example:
interface IInterface
{
void Run(TInput input);
}
abstract class AbstractClass<TInput> : IInterface
where TInput : IParameter
{
public abstract void Execute(TInput input);
public void Run(IParameter input)
{
Execute((TInput)input);
}
}
interface IParameter {}
class Implementation : AbstractClass<Parameter>
{
public void Run(Parameter input)
{
}
}
class Parameter : IParameter {}
class Program
{
static void Main()
{
object instance = new Implementation();
var castInstance = (IInterface) instance;
castInstance.Run(new Parameter());
}
}
Why don't you add a non-generic interface too:
interface IInterface
{
void Run(IParameter input);
}
And then let your generic interface extend this non-generic one.
Obviously your implementations should cast the IParameter, someone needs to cast it... You could make an abstract base class that does this for you, so not every implementation has to do this.
You might also be interested in the double dispatch pattern, although I'm not sure this will work in your case.
As you have it now, this cannot work. Your Implementation class implements IInterface<Parameter>, so its Run method only accepts a parameter of the concrete Parameter type, whereas the IInterface<IParameter> requires that its Run method accepts an instance of any type that implements IParameter.
If the type of cast you're trying to do were allowed, I could define a different class that implements IParameter, e.g.:
public class DifferentParameter : IParameter { ... }
And then do:
castInstance.Run(new DifferentParameter());
But your Implementation's Run method can't take DifferentParameter!
.NET therefore prevents you from performing the cast itself.
There are situations in which this kind of cast is allowed - if your interface were instead to be defined as:
interface IInterface<out TOutput>
where TOutput : IResult
{
TOutput Run();
}
By making the generic parameter out, it makes the interface covariant. This restricts the use of the type parameter as the result of method calls, but for covariant interfaces, casts like yours are allowed.
You can find plenty of documentation on both covariance and contravariance in the .NET documentation.

Parameter must be input-safe error

Here is a piece of my code:
public interface IA<in TInput>
{
void Method(IB<TInput> entities);
}
public interface IB<in T> { }
I can't figure out why I get following compile error:
"Parameter must be input-safe. Invalid variance: The type parameter |TInput| must be contravariantly valid on "IB< in T>".
Any help will be appreciated.
The designator of contravariance in C# (i.e. in) is intuitive only at the immediate level, when you make a method that "takes in" a parameter of generic type. Internally, however, contravariance means an inversion of a relation (Q&A with an explanation) so using in inside IA makes it incompatible with IB.
The problem is best illustrated with an example. Consider class Animal and its derived class Tiger. Let's also assume that IB<T> has a method void MethodB(T input), which is called from IA's Method:
class A_Impl<T> : IA<T> {
T data;
public void Method(IB<TInput> entities) {
entities.MethodB(data);
}
}
Declaring IA<in TInput> and IB<in TInput> means that you can do
IA<Animal> aForAnimals = new A_Impl<Animal>();
IA<Tiger> aForTigers = aForAnimals;
IA<in TInput> has a method that takes IB<TInput>, which we can call like this:
aForTigers.Method(new B_Impl<Tiger>());
This is a problem, because now A_Impl<Animal> passes an Animal to MethodB of an interface that expects a Tiger.
You would have no problem with IB<out T>, though - both with covariance and contravariance:
public interface IB<out T> {
// ^^^
}
// This works
public interface IA<in TInput> {
void Method(IB<TInput> x);
}
// This works too
public interface IC<out TInput> {
void Method(IB<TInput> x);
}

C# Generics, interfaces and inheritance

I've two interfaces:
public interface IAmA
{
}
public interface IAmB<T> where T : IAmA
{
}
And two classes implementing these interfaces like this:
public class ClassA : IAmA
{
}
public class ClassB : IAmB<ClassA>
{
}
When trying to use these classes as shown:
public class Foo
{
public void Bar()
{
var list = new List<IAmB<IAmA>>();
list.Add(new ClassB());
}
}
I get this compiler error:
cannot convert from 'ClassB' to 'IAmB<IAmA>'
I know I can make the compiler happy using:
public class ClassB : IAmB<IAmA>
{
}
But I need to be able to be the Type parameter for IAmB<> in ClassB an implementation of IAmA.
The quick answer is that you can do what you ask by declaring the type parameter of IAmB<T> as covariant, only if the type is used as a return type:
public interface IAmB<out T> where T : IAmA
{
T SomeMethod(string someparam);
}
out T means that you can use a more specific type than then one specified in the constraints.
You won't be able to use T as a parameter. The following won't compile:
public interface IAmB<out T> where T : IAmA
{
void SomeMethod(T someparam);
}
From the documentation
You can use a covariant type parameter as the return value of a method that belongs to an interface, or as the return type of a delegate. You cannot use a covariant type parameter as a generic type constraint for interface methods.
This isn't a compiler quirk.
Assuming you could declare a covariant method parameter, your list would end up containing some objects that couldn't handle an IAmB<IAmA> parameter - they would expect an input of ClassA or more specific. Your code would compile but fail at runtime.
Which begs the question - why do you want to use IAmB<ClassA> ?
You should think about before using this though, as there may be other, more suitable ways to address your actual problem. It's unusual to use a generic interface implementing a concrete type but trying to use it as if it were implementing another interface.
You can check the MSDN documentation's section on Covariance and Contravariance as well as Eric Lippert's an Jon Skeet's answers to this SO question: Difference between Covariance and Contravariance
Fast answer : make the generic type covariant (see msdn) in your interface
public interface IAmB<out T> where T : IAmA
{
}
this will resolve the compiler problem.
But this won't answer the why asked by Panagiotis Kanavos !
The trick is making the type constraint T on IAmB<T> covariant, with the out keyword:
public interface IAmB<out T> where T : IAmA
{
}
This allows you to use a more specific type than originally specified, in this case allowing you to assign an IAmB<ClassA> to a variable of type IAmB<IAmA>.
For more information, see the documentation.
I just tell why this error reported.
if your IAmB has a method
public interface IAmB<T> where T : IAmA
{
void foo(T p);
}
public class ClassB : IAmB<ClassA>
{
void foo(ClassA p)
{
p.someIntField++;
}
}
and we have another class
public class ClassC : IAmB<ClassA2>
{
void foo(ClassA2 p)
{
p.someOtherIntField++;
}
}
and we assume List<IAmB<IAmA>>.Add(T p) implement like this
IAmA mParam = xxxx;
void Add(IAmB<IAmA>> p){
p.foo(mParam);
}
thinking all compile OK. you pass a ClassB instance to List.Add, it becomes
void Add(IAmB<IAmA>> p){
//p is ClassB now
p.foo(mParam);//COMPILER CAN NOT MAKE SURE mParam fit ClassB.foo
}
It can be solved using Contravariance and Covariance.
public interface IAmA
{
}
**public interface IAmB<out T> where T : IAmA
{
}**
public class ClassA : IAmA
{
}
public class ClassB : IAmB<ClassA>
{
}
public class Foo
{
public void Bar()
{
var list = new List<IAmB<IAmA>>();
**list.Add(new ClassB());**
}
}
Now you don't get compiler error. Compiler is happy.

Cannot convert generic object

The following does not compile on line fm.AddFoo(new StringFoo()); with the error message:
Argument 1: cannot convert from 'ClassLibrary2.StringFoo' to 'ClassLibrary2.IFoo'
This seems logical to me since string inherits from object.
public interface IFoo<T>
{
void Handle(T value);
}
public class StringFoo : IFoo<string>
{
public void Handle(string value)
{ }
}
public class ObjectFoo : IFoo<object>
{
public void Handle(object value)
{ }
}
public class FooManager
{
private readonly List<IFoo<object>> _foos;
public FooManager()
{
_foos = new List<IFoo<object>>();
}
public void AddFoo(IFoo<object> foo)
{
_foos.Add(foo);
}
}
public class Bad
{
public Bad()
{
var fm = new FooManager();
fm.AddFoo(new StringFoo()); \\ This does not compile
}
}
Thanks
Although it may seem like IFoo is a subclass of IFoo it is not. When you close IFoo<> to a specific type is is not creating a subclass of IFoo from IFoo, they are seperate and distinct types with no common hierarchy.
If you could make your IFoo<> interface covariant it would work, that is if you were allowed to change the declaration of it into:
public interface IFoo<out T>
(note the out). Because with covariance any IFoo<string> would also be an IFoo<object> because string is a reference type and derives from object.
But: A member of IFoo<>, the Handle method, uses the type parameter in a contravariant manner. So your interface cannot be declared covariant (out). (It could be declared contravariant (in) but that goes in the wrong direction for your example above.)
Read up on covariance and contravariance in generics.
The fundamental problem here is that your StringFoo handles only strings. Therefore it can never be used as an IFoo<object> because then you could pass for example a Giraffe instance (Giraffe derives from object, so a Giraffe is an object) into the StringFoo, and that is impossible when its Handle takes a string.

Partially applying generics for type constraints

I currently try to construct a generic interface that every (generic) class deriving it will have a method accepting a delegate that accepts the type parameter and returns another class of the same type, with only another type parameter.
I tried the following:
public interface GenericInterface<out T, out SomeDerived>
where SomeDerived<T> : GenericInterface<T, SomeDerived>
{
SomeDerived<NT> bind<NT>(bindee<T, NT, SomeDerived<NT>> bindFunc);
}
public delegate AnotherDerived<T2> bindee<in T1, out T2, out AnotherDerived>(T1 param)
where AnotherDerived<T2> : GenericInterface<T2, AnotherDerived>;
public class Derived<T> : GenericInterface<T, Derived>
{
Derived<NT> bind<NT>(bindee<T, NT, Derived<NT>> bindFunc);
}
But it fails to compile and I get this error:
Invalid token '<' in class, struct, or interface member declaration
What is the correct design in such case?
EDIT:
I understand the syntatic reason for the compiler errors. You cannot apply a generic type argument a parameter in a where clause.
I am asking what is the best way to mimic such behavior.
I'll go out on a limb here and say what you're trying to do here with the Generic is impossible; I'll remove if someone thinks I'm wrong.
So lets start with this
interface IFoo<T> where T : IFoo<T>{}
class Foo<T> : IFoo<T> where T : IFoo<T>{}
class Bar<T> : Foo<T> where T : IFoo<T>{}
Lets try to instanciate this;
var foo = new Foo< Bar< ....errr what now? ad infinitum...
So to fix this, you need to redesign so you're classes looks more like this:
interface IBase {}
interface IFoo<out T> where T : IBase { }
class Foo<T> : IFoo<T> where T : IBase { }
which then allows:
IFoo<IBase> foo = new Foo<Base>();
[Addendum]
You can have function level generics that let you get around problems like these...
interface IFoo<out T> where T : IBase
{
IFoo<TBind> Bind<TBind>(Action<T, TBind> bindFunc) where TBind : IBase;
}

Categories

Resources