Compiler Issue with Generics and Inheritance - c#

I have 2 classes with the following declarations:
abstract class ClassBase<T, S> where T : myType where S : System.Data.Objects.DataClasses.EntityObject
abstract class ServiceBase<T> where T : myType
and I have 2 other classes, that inherit one from each, we can call ClassInherited and ServiceInherited. Note that the two Service classes are not in the same project as the other two.
The idea is that in the ServiceBase class I can declare a property like protected ClassBase<T,System.Data.Objects.DataClasses.EntityObject> Class { get; set; } and then in the inherited service`s constructor something like this.Class = ClassInheritedInstance
I already implemented the idea but it gives me this error when assigning the Class property in the ServiceInherited class constructor:
Cannot implicitly convert type 'ClassInherited' to 'ClassBase< T, S>'
Note that ClassInherited is indeed an specification of Class<T,S>... it's just that the compiler doesn't seem to be able to tell the types correctly. Also changing the declaration of the class property to protected ClassBase<T, EntityObjectInherited> works, and EntityObjectInherited is an implementation of System.Data.Objects.DataClasses.EntityObject... I don't see why is there a problem.
Update 1
Note that at compile time the type of ClassInherited is known, as its declaration is public class ClassInherited : ClassBase<myTypeInherited, EntityObjectInherited>

INITIAL ANSWER
The reason that you cannot use protected ClassBase<T,S> Class { get; set; } in the ServiceInherited-class is that you do not know the S-type that is needed to declare a type of the property Class.
You have to options:
Include the S type in the specification of the Service-type: abstract class ServiceBase<T, S> where T : myType where S : System.Data.Objects.DataClasses.EntityObject
Implement an interface for ClassBase with only the T-type, so that you can refer to a class-inherited-object without using the S-type. Then you CAN have a property in the service class (of the interface-type), since you do not need to specify the S-type.
Note that generic-type-checking is not checked at run-time, but at compile-time. Else it wouldn't be strong-typing.
UPDATE
The reason the cast won't work is that type ClassBase<T, EntityObjectInherited> is not equal or castable to ClassBase<T, System.Data.Objects.DataClasses.EntityObject>. Covariance doesn't work on class-types, only on interface-types.
I think the solution here is to work with interfaces. Use an interface for class-base, say IClassBase<T>. That way you can omit the S-type in the signature of the class, and only have it in the interface.
UPDATE (2)
One thing you can do is to create an interface for the Class property. You can define the following interface.
public interface IClass<T> where T : myType {
// TODO
// Define some interface definition, but you cannot use the
// EntityObject derived class, since they are not to be known
// in the service class.
}
If you implement this interface on your ClassBase class, and add a constructor on your ServiceBase class which accepts an object of type IClass, then you can push this object to property Class in the base-class. Like this:
public abstract class ClassBase<T, S> : IClass<T>
where T : MyType
where S : EntityObject {
}
public abstract class ServiceBase<T> where T : MyType {
protected ServiceBase(IClass<T> classObject) {
Class = classObject;
}
protected IClass<T> Class { get; set; }
}
public class ServiceInherited : ServiceBase<MyTypeDerived> {
public ServiceInherited(IClass<MyTypeDerived> classObject)
: base(classObject) {
}
}
One thing to note, is not to expose the S-type of the ClassBase to the interface. Since you do not want the Service-classes to know this type, they cannot actively call any methods or use properties that somehow have the S-type in their definition.

This ugly boxing, unboxing should work :
Class = (ClassBase<T, S>)(object)new ClassInherited();
Covariance is allowed only with generic interface today ?MSDN
This works :
// Covariance.
IEnumerable<string> strings = new List<string>();
// An object that is instantiated with a more derived type argument
// is assigned to an object instantiated with a less derived type argument.
// Assignment compatibility is preserved.
IEnumerable<object> objects = strings;
This doesn't :
List<string> strings = new List<string>();
List<object> objects = strings;

The compiler won't be able to guess that ClassInherited is indeed a correct match for ClassBase<T, S> as it does not know the exact types of T and S, that will be decided at generics type instanciation at runtime.
So if you're sure that at runtime the types will be compatible you can safely try a cast :
Class = ClassInheritedInstance as ClassBase<T, S>
This will only have a slight (not to say negligible) overhead as the CLR will need to check the compatibility of the types to have safe code.

Related

How do I correctly constrain to related class type when using a generic method?

I have two base classes BaseObject and BaseObjectSettings. The first defines the object behaviour and the second defines the state of the class (useful for serialisation).
If I want to create a derived BaseObject class with specific settings then I can use a method with a generic type constraint.
public void CreateBaseObjectInstance<T>(BaseObjectSettings baseObjectSettings) where T : BaseObject
{
var instance = pool.GetInstance<T>();
instance.Settings = baseObjectSettings;
scene.Add(instance);
}
The problem I am facing is that while I can constrain the generic type to BaseClass I can't constrain the BaseClassSettings to the relevant derived BaseClass. This means that I can do things like
CreateBaseObjectInstance<Banana>(new AppleSettings());
which seems a bit terrible.
What are my options given that I am currently constrained to both creating and initialising the object in the same method before adding it to the scene?
One way is to have all your settings classes inherit from a generic base class. The generic base class could then inherit from BaseObjectSettings. The generic type parameter indicates what kind of object this settings class is for.
For example, for your AppleSettings,
class AppleSettings: ObjectSettings<Apple> {
...
}
abstract class ObjectSettings<T>: BaseObjectSettings where T: BaseObject {}
Now, you can change CreateBaseObjectInstance to accept an instance of ObjectSettings<T> instead:
public void CreateBaseObjectInstance<T>(ObjectSettings<T> objectSettings) where T : BaseObject
{
var instance = pool.GetInstance<T>();
instance.Settings = objectSettings;
scene.Add(instance);
}
If you pass Banana as T, it would expect ObjectSettings<Banana>, preventing you from giving it AppleSettings, which is ObjectSettings<Apple>.
You need to create a generic interface or base class that where you define the settings type:
public class BaseObject<TSettings>
{
public TSettings Settings { get; set; }
}
Then your method will require two generic arguments - one for the actual object to create TObject and one for method's argument for the settings TSettings. You then constrain TObject to an implementation of the implemented interface or base class/derivation thereof, using generic argument TSettings as the constraint's type's generic argument
public void CreateBaseObjectInstance<TObject, TSettings>(
TSettings settings
)
where TObject : BaseObject<TSettings>
{
...
}
Example (using above BaseObject implementation):
public class MyObjectSettings
{
...
}
public class MyObject : BaseObject<MyObjectSettigns>
{
}
Method call:
var settings = new MyObjectSettings(){ ... };
CreateBaseObjectInstance<MyObject>( settings ); // second generic argument should be inferred
I don't really understand the logic here as things are missing, but from the code provided you can probably write:
public void CreateBaseObjectInstance<TBase, TSettings>(TSettings baseObjectSettings)
where TBase : BaseObject
where TSettings : BaseObjectSettings
Used like that:
CreateBaseObjectInstance<Banana, AppleSettings>(new AppleSettings());
Can be improved to:
public void CreateBaseObjectInstance<TBase, TSettings>(TSettings baseObjectSettings)
where TBase : BaseObject
where TSettings : BaseObjectSettings, new()
{
if ( baseObjectSettings == null ) baseObjectSettings = new TSettings();
...
}
CreateBaseObjectInstance<Banana, AppleSettings>();
But if there is a strong coupling between entity and settings, you should redesign to define dependency with an association using a thing that can also be similar to #Sweeper's and #Moho's answers:
Association, Composition and Aggregation in C#
Understanding the Aggregation, Association, Composition
Generics in .NET
Generic classes and methods

c# generics with abstract method

I'm trying to implement some generic method for an abstract class as follows :
public abstract class MyAbstractClass : MyBaseObject {
public MyAbstractClass() : base() { } // there is a parameterless constructor...
}
public class MyList<T> where T : MyBaseObject, new() {
// a generic container that is designed for the base class
}
//--- some paint control of mine
public class PaintControl : IDisposable {
public void InitDrawItems(MyList<MyAbstractClass> items) {
// paint items => this is where the compilation error occurs...
}
}
I get the following compilation error :
Error 24 'MyAbstractClass' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'MyList'
Of course, I'd like to use the abstract MyAbstractClass class (which has several children to handle painting accordingly).
Is there a way around this?
EDIT: I did make the class Abstract to make absolutely sure the children actually DO implement the abstract methods.
new() doesn't allow abstract classes and interfaces to be used as T, because they are not instantiable. new() means the generic type must declare a public parameterless constructor eligible for instantiation of an object.
So you have one option, in my opinion:
remove the new() clause, if you are ok with any abstract type derived from MyBaseObject being used with your generic. As your class is indeed abstract and a child of MyBaseObject, that would work just fine.
This is problem is caused by generic type variance. Declaring a concrete type actually creates a new type at compile time. The type parameters in a class can't be converted implicitly between types. There's no inheritance relation between MyList<MyBaseObject> and MyList<MyConcreteObject>.
Conversions are only permitted for generic interfaces or delegates.
There are two ways to fix this - use an interface instead of a concrete class, eg :
class MyList<T>:IList<T> where T : MyBaseObject, new()
{
}
class PaintControl {
public void InitDrawItems<T>(IList<MyAbstractClass> items) //where T:MyAbstractClass,new()
{
//var anItem=new T();
}
}
Or make InitDrawItems itself generic:
public void InitDrawItems<T>(MyList<T> items) where T:MyAbstractClass,new()
{
// No compilation errors here
}
You should remove the new() constraint for T type in MyList, since it's specifying that you can only use types that can be initialize - abstract ones can't.
You can check more details here: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/where-generic-type-constraint
The answer seems a bit of a workaround, but it actually makes lots of sense...
I know for sure which item of the list I am working with (I am looping through items). Since this item has been created, it is FOR SURE a non-abstract class. And hence, I can call the activator for that item, instead of new T() :
public void InitDrawItems(MyList<MyAbstractClass> items)
foreach (var item in items) {
Type type = o.GetType();
// type is definitely NOT abstract, since the object was created, so it is a
// non-abstract child class and I create the right "copy" of the item !
MyAbstractClass newItem = Activator.CreateInstance(type) as MyAbstractClass;
// ... do whatever I need with the item
}
}
NOTE: as for generics, it makes sens that calling new T() isn't allowed for abstract classes. Put it that way : a container of generic abstract items contains items of a derived child class since the abstract parent class cannot be instantiated. So calling new T() would definitely lead to an issue if allowed by the compiler : which child class should it be?!?
As #Martin pointed out in the comments, from the docs:
The new constraint specifies that any type argument in a generic class declaration must have a public parameterless constructor. To use the new constraint, the type cannot be abstract.
And the code you have written to define MyList<T>:
public class MyList<T> where T : MyBaseObject, new()
says MyList<T> is a generic class where T must have the new constraint (be type that you can instantiate).
So the reason of your compilation error on the code:
public void InitDrawItems(MyList<MyAbstractClass> items)
is because T in this case is MyAbstractClass which is an abstract class which cannot be instantiated.
So you options are as follows:
Make the method generic, like so:
public void InitDrawItems<T>(MyList<T> items) where T : MyAbstractClass, new()
{
// paint items => this is where the compilation error occurs...
}
Remove the new constraint from T in MyList
Make MyAbstractClass a normal class and not abstract.
It's very simple, you can't instantiate an abstract class and so T = MyAbstractClass clashes with the where T : new() constraint.

.NET: class definition explanation

I understand basic inheritance and I understand the basics of generics.
But I do NOT understand this class definition:
public class ExportController : AbstractFeedController<IExportFeed>
The ExportController inherits AbstractFeedController...
but, what does the <IExportFeed> do/mean? Is it something to do with generics?
An Introduction to C# Generics chapter Inheritance and Generics:
When deriving from a generic base class, you must provide a type argument instead of the base-class's generic type parameter:
public class BaseClass<T>
{...}
public class SubClass : BaseClass<int>
{...}
If the subclass is generic, instead of a concrete type argument, you can use the subclass generic type parameter as the specified type for the generic base class:
public class SubClass<T> : BaseClass<T>
{...}
This means that your class ExportController is no longer generic and is derived from class AbstractFeedController<IExportFeed>.
Yes, it is a generic definition. In short, AbstractFeedController defines a generic implementation that can be applied to various Types including IExportFeed in your case.
Look at the definition of the class AbstractFeedController, you will likely see something like
class AbstractFeedController<T>{ ...
In the class you will then see the Type T used multiple times. Whenever you see this T, you can swap it in your mind with any Type you think can apply.
In the class definition, you might also see a where T : .... This is a condition on the Type T, limiting the kind of Types the class can use.
Read this MSDN Article for in-depth explanation.
In plain english it means that ExportController is deriving from a closed generic typed class AbstractFeedController<IExportFeed>. AbstractFeedController class has some of the methods, properties, fields, indexers etc whose types or return types or parameter types could be of type IExportFeed .
So AbstractFeedController class may look like this
//This is a open type:
public class AbstractFeedController <T>
{
T[] m_Items;
public void Feed(T item)
{...}
public T ReturnFeed()
{...}
}
Now we close the type by intansiating a class with IExportFeed as generic type parameter
AbstractFeedController feedController = new AbstractFeedController<IExportFeed>();
So the class internally translates as follows:
//This is a closed type now:
public class AbstractFeedController <IExportFeed>
{
IExportFeed[] m_Items; //Indexer type of IExportFeed
public void Feed(IExportFeed item) //A method accepting a parameter of type IExportFeed
{...}
public IExportFeed ReturnFeed() //A method returning type of IExportFeed
{...}
}
Imagine the situation like
public class ExportController : IExportFeed
public class ImportController : IImportFeed
let's assume that in export case we have some operations common for all implementers of IExportFeed. So may move these operations into some base abstract class for IExportFeed hierarchy.
Same for IImportFeed.
But what if we have some common operations for both these hierarchies?
we can do something like
public abstract class ImportExportController : IExportFeed, IImportFeed
and inherit export or import classes from this one.
But this design breaks minimum couple of SOLID principles, and it's going to be a mess if you decide to add anoter feed interfaces
The solution is to move this common inter-hierarchy functionality into a generic(template)
public class ExportController : AbstractFeedController<IExportFeed>
public class ImportController : AbstractFeedController<IImportFeed>
etc
The inherited class implicitly defines the type of the generic class.
So the inherited class is not generic anymore, and uses the base class for the provided type.

Can you subclass a generics class with a specific typed class?

I have a generics class with a subclass that provides specific types.
public abstract class GenericBase<T>
where T:Interface1
{
}
I subclass the generics with specific implementations:
public class Customer:
GenericBase<Type1>
(Type1 implements Interface1).
I have another abstract base class that has a reference to this:
protected GenericBase<Interface1> genericInstance;
Finally, when I attempt to assign the genericInstance to an instance of the base class, it gives me a compiler error, saying that it "cannot implicitly convert Customer to GenericBase<Interface1>".
base.genericInstance = new Customer(); //Compiler error
I don't understand why I would get this error if Customer is a subtype of GenericBase<Type1>, and Type1 implements Interface1. Isn't Customer effectively a type of GenericBase<Interface1>, if it's a subclass of GenericBase<Type1>?
I assume I'm misunderstanding something about generics here; is there a way to allow this behavior?
In C#, covariance (assigning a derived type to a base type) cannot be applied to generic classes. As a result, you would need to apply an interface specifically marked as covariant, using the out parameter modifier on a new IGenericBase interface.
protected IGenericBase<Interface1> genericInstance = new Customer();
public interface IGenericBase<out T> {}
public abstract class GenericBase<T> : IGenericBase<T>
where T:Interface1 {}
public interface Interface1 {}
public class Type1 : Interface1 {}
public class Customer: GenericBase<Type1> {}
C# doesn't have covariance of generic classes, which basically means you cannot assign a value with a more derived type argument to a variable with a less derived type argument.
That would work with interfaces, though, provided some conditions are satisfied, namely, if the parameter-type is used in covariant positions only, i.e. as a return type of methods and properties.
Refer to this and other bits of documentation for more info.

What is this C# construct doing and why? MyClass<TMyClass> : MyClass where

I came across the following code, all in a single file/class. I'm leaving out the details, its the construct I'm interested in. Why are there two different declarations for the same class and how are they different? What's the purpose of the syntax for the second declaration?
public abstract class MyClass
{
...
}
public abstract class MyClass<TMyClass> : MyClass
where TMyClass: MyClass<TMyClass>
{
...
}
MyClass - A abstract class named MyClass.
MyClass<TMyClass> : MyClass - A abstract generic class named MyClass<> but with a generic type named TMyClass.
If you rename the types, it will be easier to see:
public abstract class MyBaseClass
{
...
}
public abstract class MyClass<T> : MyBaseClass
where T: MyClass<T>
{
...
}
Types with different generic arity (i.e. number of generic type parameters, which can be zero or more) are considered as completely unrelated by the language and can have the same name.
This means that you can have classes Foo, Foo<T> and Foo<T,U> at the same time; the syntax will allow the compiler to determine which you are referring to. You can see this happen in the base framework which includes Action, Action<T> etc.
The "recursive" construct class C<T> where T: C<T> (the inheritance from a non-generic C does not change anything so I removed it) is the C# on what is called the Curiously Recurring Template Pattern (CRTP) in C++. Eric Lippert has covered this subject very well in a blog post, where the conclusion is that one should think more than twice before implementing this -- there are problems it can solve, but the solution also has a price.
public abstract class MyClass<TMyClass> : MyClass
where TMyClass: MyClass<TMyClass>
{
...
}
is a class that inherits from MyClass, and it takes a generic type, which has to inherit from MyClass<TMyClass>
Here's a simpler example of the same thing for you
public static void Main()
{
MyClass<Myclass> other = new MyClass<Myclass>(new Myclass());
List<int> intlist = new List<int>();
}
public class Myclass
{
public Myclass()
{
}
public int i { get; set; }
}
public class MyClass<T> where T : Myclass
{
T value;
public MyClass(T val)
{
value = val;
}
}
}
It's a classic idiom, the Curiously Recurring Template Pattern, done in C#.
It means the template can only be used thusly:
class Foo : MyClass<Foo>
{
}
In this construction, Foo inherits MyClass<Foo> which inherits MyClass.
This has a few advantages, but I've forgotten which.
public abstract class MyClass<TMyClass> : MyClass
where TMyClass: MyClass<TMyClass>
{
...
}
First thing to point out is that this is an abstract class that is inheriting from another abstract class. In other words, this is a class that cannot be instantiated (without another class inheriting from it), but is using inheritence to derive the functionality from another abstract class (which is fine).
The second thing to point out, is that this is a Template class (or generic class as they call it in C#) which accepts type in it's . I would reduce that to T as a convention so that T is always a template, though it is completely up to you what you call things.
Lastly there is a constraint on this which is kind of strange. It says that, no matter what, the compiler will not allow any class type to be passed in as a template type, unless it inherits from (somewhere along the inheritance chain)
MyClass<TMyClass>
This is shown in the following line
where TMyClass: MyClass<TMyClass>
basically this prevents anyone from passing in an object that does not follow this rule.
What is a bit odd is that the constraints tells the implementer that it cannot be a template unless the type passed by template is in fact a type of itself. You as the designer of this class (or implementer) has to decide if this is a wise design, though this in itself looks a bit strange.

Categories