Below are working classes:
public class CatalogManager<T1, T2, T3> where T1 : CatalogDataEntryForm<DataEntryControl>, new()
where T2 : CatalogDataGridForm<DataGridControl>, new()
where T3 : CatalogBusinessObject
{
public CatalogManager()
{
_DataGridFrom = new T2();
InitGridformToolbarItemeEvents();
}
}
public class BankDataEntryForm : CatalogDataEntryForm<BankDataEntryControl>
{
}
public class BankDataGridForm : CatalogDataGridForm<BankDataGridControl>
{
}
However, below derived class is complaining with error:
public class BankManager : CatalogManager<BankDataEntryForm, BankDataGridForm, BankBo>
{
public BankManager()
{
}
}
Error message:
Error CS0311 The type 'BankDataEntryForm' cannot be used as type
parameter 'T1' in the generic type or method 'CatalogManager'. Error CS0311 The type 'BankDataGridForm' cannot be used as type
parameter 'T2' in the generic type or method 'CatalogManager'
Many thanks for your help.
The issue is a Covariance and Contravariance in Generics as SLaks say DataEntryControl is not the same as BankDataEntryControl, although They are an inheritance relationship.
Starting with the .NET Framework 4, Visual Basic and C# have keywords that enable you to mark the generic type parameters of interfaces and delegates as covariant or contravariant.
so you can try to make the interface for those class.
ICatalogDataEntryForm<out T> for CatalogDataEntryForm<T>
ICatalogDataGridForm<out T> for CatalogDataGridForm<T>
then let those class implement interface
public interface ICatalogDataEntryForm<out T>
{ }
public interface ICatalogDataGridForm<out T>
{ }
public class CatalogDataEntryForm<T> : ICatalogDataEntryForm<T>
{ }
public class CatalogDataGridForm<T> : ICatalogDataGridForm<T>
{}
BankDataGridForm and BankDataEntryForm no need to change.
public class BankDataGridForm : CatalogDataGridForm<BankDataGridControl>
{
}
public class BankDataEntryForm : CatalogDataEntryForm<BankDataEntryControl>
{
}
public class BankManager : CatalogManager<BankDataEntryForm, BankDataGridForm,CatalogBusinessObject>
{
public BankManager()
{
}
}
Then let CatalogManager class contract with those interface
public class CatalogManager<T1, T2, T3> where T1 : ICatalogDataEntryForm<DataEntryControl>, new()
where T2 : ICatalogDataGridForm<DataGridControl>, new()
where T3 : CatalogBusinessObject
{
public CatalogManager()
{
}
}
c# online
Related
To define a generic class:
public class TParent<T1, T2> { }
public class ChildManager<T1, T2, T3> where T3 : TParent<T1 , T2> { }
public class Child : TParent<int , double> { }
The class Child is an avaliable T3 type for ChildManager , but the class definiton of ChildManager is too long.
The T3 is derived from Tparent , and the T1,T2 has been already defined in TParent.
So, can the definiton be like such code?
public class ChildManager<T3> where T3:TParent<classtype,classtype> { }
However,such code is an error.
Why does this code gives this error?
Argument type 'System.Collections.Generic.IEnumerable<T>' is not
assignable to parameter type
'System.Collections.Generic.IEnumerable<[...].IExample>'
public interface IExample { }
public class BaseClass
{
public BaseClass(IEnumerable<IExample> a) { }
}
public class FailingClass<T> : BaseClass
where T : IExample
{
public FailingClass(IEnumerable<T> a): base(a) { } //error here on base(a)
}
You are missing the class constraint to T within FailingClass.
IEnumerable<T> has a type parameter marked as covariant. Covariance enables you to use a more derived type than originally specified. Variance in general applies to reference types only.
So what the class constraint actually does here is enforcing a constraint to pass a reference type. If you were to pass in a value type for T, that type parameter is invariant for the resulting constructed type and does not suffice IEnumerable<T>.
using System.Collections.Generic;
public interface IExample { }
public class BaseClass
{
public BaseClass(IEnumerable<IExample> a) { }
}
public class FailingClass<T> : BaseClass
where T : class, IExample
{
public FailingClass(IEnumerable<T> a): base(a) { } //error here on base(a)
}
Hi, I have a variant factory interface. Now, I am deriving factories from it and would like to be able to pass a factory of more derived type as a factory of less derived type. It doesn't work and I am really trying to figure out why as from the little I know about variance, it should.
Please consider this code:
public interface IFactory<in TParam, out TValue> { }
public class Fruit { }
public class Apple : Fruit { }
public class BoxOfFruits { }
public class BoxOfApples : BoxOfFruits { }
public class BoxOfFruitsFactory : IFactory<Fruit, BoxOfFruits> { }
public class BoxOfApplesFactory : IFactory<Apple, BoxOfApples> { }
public class FruitPacker
{
public FruitPacker(IFactory<Fruit, BoxOfFruits> factory) { }
}
public class ApplePacker : FruitPacker
{
//This works fine as expected, as there is no variance in play.
public ApplePacker(BoxOfFruitsFactory factory) : base(factory) { }
//This complains about not being able to convert BoxOfApplesFactory to IFactory<Fruit, BoxOfFruits>
public ApplePacker(BoxOfApplesFactory factory) : base(factory) { }
//This is not working either
public ApplePacker(IFactory<Apple, BoxOfApples> factory) : base(factory) { }
//But this does work
public ApplePacker(IFactory<Fruit, BoxOfApples> factory) : base(factory) { }
}
Could someone please explain, why this is not working? My assumtions are that "in" lets me pass in more derived type and "out" lets me output less derived type.
Thanks
Having
public interface IGeneric<T>{}
public class Student{}
public class Teacher{}
this is possible
public class ConcreateClass : IGeneric<Student>, IGeneric<Teacher>{}
this is not possible
public class GenericClass<T1, T2> : IGeneric<T1>, IGeneric<T2> {}
because GenericClass<String, String> instance; would cause ambiguity for the two interface implementation. But why this is not possible
public class GenericClassWithTypeConstraints<T1, T2> : IGeneric<T1>, IGeneric<T2>
where T1 : Student
where T2 : Teacher
{}
as T1 and T2 cannot be of the same class? (The compiler error is the same as in the case without type constrains)
Edit
"Soner Gönül" in Why does this result in CS0695 proposed a workarround, using two levels of class inheritance, like this:
public interface IGeneric<T> { String Name { get; } }
public class Student{}
public class Teacher{}
public class GenericClassBase<T1> : IGeneric<T1>
where T1 : Student
{ String IGeneric<T1>.Name { get { return "Generic class of Student"; } } }
public class GenericClassDerived<T1, T2> : GenericClassBase<T1>, IGeneric<T2>
where T1 : Student
where T2 : Teacher
{ String IGeneric<T2>.Name { get { return "Generic class of Teacher"; } } }
Code like this then produce expected result
GenericClassDerived<Student, Teacher> X = new GenericClassDerived<Student, Teacher>();
Console.WriteLine(((IGeneric<Student>)X).Name); //outputs "Generic class of Student"
Console.WriteLine(((IGeneric<Teacher>)X).Name); //outputs "Generic class of Teacher"
as T1 and T2 cannot be of the same class?
Yes, they can be the same class. Constraints cannot be sealed classes1 so Teacher can be derived from Student or vise versa. It's not logical but compiler has no idea about it.
For example,
using System;
public class Student{}
public class Teacher : Student{}
public class GenericClassWithTypeConstraints<T1, T2>
where T1 : Student
where T2 : Teacher
{}
class Test {
static void Main() {
var obj = new GenericClassWithTypeConstraints<Teacher, Teacher>();
}
}
will compile without any problem. Demo
Docs:
Constraints can also be class types, such as abstract base classes.
However, constraints cannot be value types or sealed classes.
I have a generic class with a class constraint on it.
public class MyContainer<T> where T : MyBaseRow
MyBaseRow is an abstract class which I also want to contain a member of some flavour of MyContainer.
public abstract class MyBaseRow
{
public MyContainer<MyBaseRow> ParentContainer;
public MyBaseRow(MyContainer<MyBaseRow> parentContainer)
{
ParentContainer = parentContainer;
}
}
I am having problems with the constructors of classes inherited from MyBaseRow eg.
public class MyInheritedRowA : MyBaseRow
{
public MyInheritedRowA(MyContainer<MyInheritedRowA> parentContainer)
: base(parentContainer)
{ }
}
Won't allow MyInheritedRowA in the constructor, the compiler only expects and only allows MyBaseRow. I thought the generic class constraint allowed for inheritance? What am I doing wrong here and is there any way I can redesign these classes to get around this?
Many thanks in advance for any responses.
Basically, you can't use generics that way, because the covariance system doesn't work that way with classes. See here: http://geekswithblogs.net/abhijeetp/archive/2010/01/10/covariance-and-contravariance-in-c-4.0.aspx
You can however use an interface like this:
public interface MyContainer<out T> where T : MyBaseRow {
}
And that code will compile.
You can make a covariant generic interface (C#4.0):
public interface IContainer<out T> where T : MyBaseRow
{
}
public class MyContainer<T> : IContainer<T> where T : MyBaseRow
{
}
public abstract class MyBaseRow
{
public IContainer<MyBaseRow> ParentContainer;
public MyBaseRow(IContainer<MyBaseRow> parentContainer)
{
ParentContainer = parentContainer;
}
}
public class MyInheritedRowA : MyBaseRow
{
public MyInheritedRowA(IContainer<MyInheritedRowA> parentContainer)
: base(parentContainer)
{ }
}