C# Generics, interfaces and inheritance - c#

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.

Related

c# .net 4.0 Covariant vs Contravariant

I'm trying to get something working and struggling with the below when using Contravariance. My understanding is Covariance is where you can return a derived type from a base type. Contravariance is where you can pass in a derived type from a base type as an argument into a class.
So I have the below interface (contravariant):
public interface IBase<in T> where T: BaseModel
{
void Process(T model);
}
I then have an abstract class
public abstract class Base<T>: IBase<T> where T: BaseModel
{
public virtual void Process(T model)
{
// throw new System.NotImplementedException();
}
}
and another concrete class
public class Parent: Base<ParentModel>
{
public override void Process(ParentModel model)
{
// throw new System.NotImplementedException();
}
}
Considering the generic type is only ever used as an input and not a return type, I don't see why I cannot do the below:
IBase<BaseModel> baseContravariant = new Parent();
// This doesn't compile. I will eventually have a list of IBase<BaseMode> to which I'd like to pass in different parent instances.
I have another example using covariance which is below and works fine.
public interface IBase<out T> where T : BaseModel, new()
{
T ProcessAndGet();
}
Abstract
public abstract class Base<T>: IBase<T> where T: BaseModel, new()
{
public virtual T ProcessAndGet()
{
var result = new T() as BaseModel;
// More shizzle here
return (T)result;
}
}
Concrete
public class Parent : Base<ParentModel>
{
public override ParentModel ProcessAndGet()
{
var x = base.ProcessAndGet();
return x;
}
}
Now I can do
IBase<BaseModel> baseInstance = new Base<BaseModel>();
IBase<BaseModel> derived = new Parent();
baseInstance = derived;
There's more code to the above examples but I've removed it for ease of reading (hopefully!) :-)
Contravariance in this case means that you need to pass in types that are of the specified type or one that is "more specialised" (=> derive from the base type).
Since your Parent implementation in your first example can only process ParentModel, it is invalid to pass in a BaseModel instance. Trying to do new Parent().Process(new BaseModel()) also would not compile. So it is invalid to cast it to IBase<BaseModel>. (assuming ParentModel is a subclass of BaseModel).
In this case the contravariance model is easier to reason about by thinking that an IBase<in T> "consumes a T". So an IBase<ParentModel> "consumes ParentModels". This means it can only be passed values that are instances of ParentModel or can be treated as one (effectively only subclasses).
In your second example you are using <out T>, which is "covariant". This can be described as "it produces instances of T". So a class that "produces" a ParentModel is automatically a "producer" of BaseModel as well: since ParentModel can be cased to BaseModel, IBase<ParentModel> can be casted to IBase<BaseModel> as well.

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);
}

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.

Generic inheritance in C#: Cannot implicitly convert type 'MyWidget' to 'IWidget'

I'm having a problem setting up inheritance with generics.
In essence what I want to do is have a generic interface that itself accepts an interface. the tricky part is that "inner" interface could have a more specific interface layered on top of it.
Here is a representative sample of the structure that I'm trying to build:
public interface IThing { }
public interface IMoreSpecificThing : IThing { }
public interface IWidget<T> where T : IThing { }
public class MySpecificThing : IMoreSpecificThing { }
public class MyWidget : IWidget<MySpecificThing> { }
public class MyClass
{
public IWidget<IThing> MyProperty { get; set; }
public MyClass()
{
MyProperty = new MyWidget();
}
}
The problem is that when I assign MyWidget to MyProperty, I receive the following error:
Cannot implicitly convert type 'MyWidget' to 'IWidget<IThing>'. An
explicit conversion exists (are you missing a cast?)
What am I doing wrong, and is there a way to do this properly?
This would require making your interface covariant:
public interface IWidget<out T> where T : IThing { }
For details, see Covariance and Contravariance in Generics. Note that this does put restrictions on the interface, mainly:
All the type parameters of these interfaces are covariant, so the type parameters are used only for the return types of the members.

Generic Constraint to allow casting from interface to implementation

I have a small class that implements a dictionary that maps from the type of an interface to an implementation of that interface that extends from a base class. Unfortunately the abstract base class does not implement the interfaces, so once in the dictionary, there seems to be no way to associate the two. There is another method in this class that is dependent on storing the objects as BaseClass (in fact, most of my class is dependent on that--the getter into the dictionary is somewhat of a convenience).
private readonly Dictionary<Type, BaseClass> dictionary;
public void Add<T>(BaseClass base)
{
if (!(base is T)) // How to get rid of this check?
{
throw new ArgumentException("base does not implement " + typeof(T).Name);
}
this.dictionary.Add(typeof(T), base);
}
public T Get<T>()
{
BaseClass base;
this.dictionary.TryGetValue(typeof(T), out base);
return (T)(object)base; // How to get rid of (object) cast?
}
Are there any clever constraints I can use to remove the (base is T) check, the cast to object, or both?
Here is the class setup, for reference:
class BaseClass { }
interface IThing { }
class MyClass : BaseClass, IThing { }
dict.Add<IThing>(new MyClass());
IThing myClass = dict.Get<IThing>();
The only way to get the compile-time enforcement you're looking for would be if you have compile-type knowledge of the derived type being added.
For example, if you also specify a type parameter for the class being added then you could constrain that the class implement the interface type parameter:
public void Add<TInterface, TClass>(TClass #base)
where TClass : BaseClass, TInterface {
this.dictionary.Add(typeof(TInterface), #base);
}
So you could do this:
MyClass ok = new MyClass();
dict.Add<IThing, MyClass>(ok);
But not this:
class MyClassNotIThing : BaseClass { }
MyClassNotIThing notOk = new MyClassNotIThing();
dict.Add<IThing, MyClassNotIThing>(notOk);
Aside from that, generic constraints don't offer a means by which to constrain that a known type (i.e. BaseClass) inherit from a generic type parameter.
Here is the solution I ended up using. There are a few tricks that can make the Add() safe without the check (see the link in a comment to cokeman19's answer), but I opted not to do that as I find this code a bit cleaner.
interface IThing { }
abstract class BaseClass
{
internal T AsInterface<T> where T : class
{
return this as T;
}
}
class MyClass : BaseClass, IThing { }
class DictionaryClass
{
private readonly Dictionary<Type, BaseClass> dictionary;
public void Add<T>(BaseClass base)
{
if (base is T)
{
dictionary.Add(typeof(T), base);
}
}
public T Get<T>() where T : class
{
return dictionary[typeof(T)].AsInterface<T>();
}
}
Note that this solution does allow calls like:
myClass.AsInterface<IThingItDoesntImplement>()
but this returns null and I made the function internal to prevent strange uses anyway.

Categories

Resources