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.
Related
If I write a method accepting a parameter which derives from a BaseClass (or an interface), as far as I know there are two ways to achieve that:
void MyMethod<T>(T obj) where T : BaseClass { ... }
and
void MyMethod(BaseClass obj) { ... }
What are the differences between the two methods?
In this example there isn't a big difference between the two, you can access the same members inside the method and you can call it with the same derived classes. There is a runtime difference as a generic method is compiled for each type it is invoked with.
Where generics come in useful would be if you would return a value depending on T
With generics you could do the following
T MyMethod<T>(T obj) where T : BaseClass { ... }
MyMethod(derivedInstance).derivedProperty
Without this would be an error:
BaseClass MyMethod(BaseClass obj) { ... }
MyMethod(derivedInstance).derivedProperty // error
Note Although you mention constraining to a base class, it is worth mentioning that if you constrain not to a class, but to an interface, extra boxing will occur if the implementation is by a struct in the non generic version, this can have severe performance implications.
When T is constrained to a base class, there is not really much difference apart from what has already been stated.
When T is constrained to an interface, the difference can be huge:
int FrobNonGeneric(IFrobbable frob) { //... }
int Frob<T>(T frob) where T: IFrobbable { //... }
struct Frob: IFrobbable { ... }
FrobNonGeneric(new Frob()); //boxing!
Frob(new Frob()); //no boxing
Definitely the example you quoted does not make much difference other than run time execution performance as mentioned in other answers.
Leaving aside generic collections benefits (performance improvement by avoiding boxing/unboxing for example) which we all aware of and we use frequently - Generics also works great from a consumer perspective. For example, the below code snippet is self explanatory to visualize API usage flexibility from a consumer perspective :
interface IEntity
{
int Id {get;set;}
}
class Student : IEntity
{
int Id {get;set;}
string SubjectOpted {get;set;}
}
class Employee : IEntity
{
int Id {get;set;}
string DepartmentName{get;set;}
}
interface INonGenericRepository
{
IEntity Get(int id)
}
interface IGenericRepository<T> where T:Entity
{
T Get(int id)
}
class NonGenericRepository : IRepository
{
public IEntity Get(int id) {/*implementation goes here */
}
class GenericRepository<T> : IRepository<T>
{
public T Get(int id) {/*implementation goes here */
}
Class NonGenericStudentConsumer
{
IEntity student = new NonGenericFRepository().Get(5);
var Id = student.Id
var subject = student.SubjectOpted /*does not work, you need to cast */
}
Class GenericStudentConsumer
{
var student = new GenericFRepository<Student>().Get(5);
var Id = student.Id
var subject = student.SubjectOpted /*works perfect and clean */
}
A couple of other use cases promoting flexibility while using generics along with constraints are :
Lets say I want to ensure parameter passed to method implements IAdd and IMultiply and I have class which implements both IAdd,IMulitply like :
public class BusinessOpeartion<T> where T : IAdd, IMultiply{
void SomeBusinessOpeartion(T obj) { /*implementation */}
}
If I need to go via non generic approach, I am forced to create redundant dummy interface like :
interface IDummy : IAdd, IMultiply
public class BusinessOpeartion{
void SomeBusinessOpeartion(IDummy obj) { /*implementation */}
}
Isn't the former approach cleaner?
Also one more small thing just popped up while typing answer. In case you need to, how would you get new instance for parameter type inside method:
you cannot do
IDummy dummy = new IDummy(); /*illegal*/
But with generic you could have; T temp = new T(); provided there is constraint of new()
Also what if you need a default value for parameter type?
you cannot do
var default = default(IDummy); /*illegal*/
But with generic you could have; var default = default(T)
As was said, it matters only once you get a return value. Consider these cases:
BaseClass MyMethod(BaseClass)
DervivedClass temp = new DervivedClass();
//Error. My Method always returns a BaseClass. No implicit casting available
temp = MyMethod(temp);
Compare it to this:
T MyMethod<T>(T) where T : BaseClass
DervivedClass temp = new DerivedClass();
temp = MyMethod<DerivedClass>(temp);
Strong Typification is one of the best friends you have in .NET. Embrace it. Never try to avoid it. The opposite would be cases like we have in PHP and JavaScript: http://www.sandraandwoo.com/2015/12/24/0747-melodys-guide-to-programming-languages/
In the examples included in your question, there isn't much difference between the generic and the non-generic version. But here are some other examples of method signatures that can't be expressed without generics:
T MyMethod<T>(T obj) where T : BaseClass { ... }
void MyMethod<T>(T obj1, T obj2) where T : BaseClass { ... }
void MyMethod<T>(T obj, List<T> list) where T : BaseClass { ... }
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.
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.
Why is this not possible?
abstract class A
{
public abstract T f<T>();
}
class B<T> : A
{
public override T f()
{
return default (T);
}
}
Errors:
does not implement inherited abstract member 'A.f<T>()'
no suitable method found to override
I know that the signature must be same, but from my point of view I see no reason what could possibly be wrong that this is forbidden.
Also I know that another solution is to make A generic, rather than its method, but it is not suitable for me for some reason.
This is not possible because those methods have different signatures. A.f is generic method and B.f is not (it merely uses class generic argument).
You can see this form caller perspective:
A variableA = new A();
variableA.f<int>();
B<int> variableB = new B<int>();
variableB.f();
B does not fulfil the contract of A.
A allows f to be called with any type parameter to return that type. B doesn't allow f to be called with a type parameter, and just returns the type of B's type parameter.
For example, say you had a B<int> and cast it to an A (which should be possible as it inherits from it). Then you called f<bool>() on it (which should be possible as it's an A). What then? The underlying B<int> doesn't have a method to call.
B b = new B<int>();
// This is legal as B inherits from A
A a = b;
// This is a legal call, but how does b handle it?
bool result = a.f<bool>();
In the case of your code
abstract class A
{
public abstract T f<T>();
}
class B<T> : A
{
public override T f()
{
return default (T);
}
}
what do you expect to be called in the below code
public void Foo(A myObj) {
myObj.f<DateTime>();
}
Foo(new B<int>());
There's no implementation for that method though the type contract (the abstract class A) clearly states that you need an implementation. So you can either implement or change the contract to use a type argument at the class level
abstract class A<T>
{
public abstract T f();
}
class B<T> : A<T>
{
public override T f()
{
return default (T);
}
}
does compile however it also limits f of course
Probably this is what you intend to do:
abstract class A
{
public abstract T f<T>();
}
class B<U> : A
{
public override T f<T>() //also needs to have a generic type parameter
{
throw new NotImplementedException();
}
public U f()
{
return f<U>();
}
}
The generic method type parameter and the generic class type parameter (here T and U) have no straightforward connection, i.e. T is not restricted to be U (or something) in the base class and you cannot change this restriction in the derived class.
abstract class A
{
public abstract T f<T>();
}
class B<T> : A
{
public override T f<T>()
{
return default (T);
}
}
What differences are there between declaring:
GenericClass<T> genericInst = new GenericClass<T>();
and
GenericClass<baseClass> temp = new GenericClass<baseClass>();
Here the GenericClass is defined to be for where T : baseClass
GenericClass contains a generic list
private List<T> vals = new List<T>();
It seems to me, and please correct me if I'm wrong, that you are taking 'where T : baseClass' as if it were a default for type T to be a baseClass?
If so, this is not the case, the specialization where T : baseClass means that the type T must be baseClass or derived from baseClass (or implement if it were an interface instead of a class).
Thus, if you had:
public class GenericClass<T> where T : baseClass
{
}
Then you can say:
var x = new GenericClass<baseClass>();
Or
var y = new GenericClass<SomethignDerivedFromBaseClass>();
But you could not say:
var z = new GenericClass<int>();
Since int does not inherit from baseClass.
The only way to actually use T in the instantiation above is if you were actually calling that line of code from within the GenericClass<T>:
public GenericClass<T> where T : baseClass
{
void SomeMethod()
{
GenericClass<T> genericInst = new GenericClass<T>();
}
}
Or from another context where T is already known to be a sub-class of baseClass.
UPDATE
Based on your comments, it sounds like you're wondering that if you had:
public class GenericClass<T> where T : baseClass
{
public List<T> Items { get; set; }
...
}
Whether you could add things derived from baseClass into the list, and the answer is yes-ish. The class GenericClass<T> could be declared for any T that inherits from baseClass. But the List<T> would still be strongly typed to type T.
That is given these:
public class BaseClass { }
public class SubClass : BaseClass { }
A GenericClass<BaseClass> could hold both BaseClass and SubClass in it's List<T> since T will be BaseClass.
But a GenericClass<SubClass> will have a List<T> where T will be SubClass and thus can only hold items of SubClass or inheriting from it.