A little bit confusion with C# Generics, T, Abstraction ? - c#

I am a C# game maker with Unity. I have collectable management system.
CollectableManager
public List<CollectableParent<Collectable>> collactableParentsList;
CollectableParent
public class CollectableParent<T> : CollectableRelatedMonoBehaviour where T : Collectable
SpawnPointDefinedCollectableParent
public class SpawnPointParentDefinedCollectableParent<T> : CollectableParent<T> where T : Collectable
Collectable
public abstract class Collectable : CollectableRelatedMonoBehaviour, IHasPlayableSound
Collectable_Money
public class Collectable_Money : Collectable
CollectableParent_Money
public class CollectableParent_Money : SpawnPointParentDefinedCollectableParent<Collectable_Money>
PROBLEM
"collactableParentsList" in CollectableManager does not accept SpawnPointParentDefinedCollectableParent<Collectable_Money> as item when T is defined as "Collectable_Money", it is derived from "Collectable". If I do this SpawnPointParentDefinedCollectableParent<Collectable>, it is accepted as item into list.

The problem here is that even though Collectable_Money is a sub-class of Collectable, that does not make SpawnPointParentDefinedCollectableParent<Collectable_Money> a sub-class of CollectableParent<Collectable>.
It may be possible to fix this by using generic interfaces. If we change CollectableParent<T> to be an interface, and define it to be covariant in T via the out modifier. (You haven't provided the definition for CollectableRelatedMonoBehaviour, but it will also need to be converted to an interface):
public interface ICollectableParent<out T> : ICollectableRelatedMonoBehaviour where T : Collectable
If you then define the list as:
public static List<ICollectableParent<Collectable>> collectableParentsList;
Then you can successfully add items of type SpawnPointParentDefinedCollectableParent<Collectable_Money> to it.
The critical part here is that the ICollectableParent<T> interface is covariant in T. This makes it possible to pass an instance that implements ICollectableParent<Collectable_Money> where an ICollectableParent<Collectable> is expected.
Defining the interface as covariant in T does introduce some restrictions, specifically (and as implied by the out modifier used to indicate covariance), the type T must only be used as a return value on the interface's methods. For example:
public interface ICollectableParent<out T> : ICollectableRelatedMonoBehaviour where T : Collectable
{
// This is allowed - T is used as a return type
T GetChild();
// This is *not* allowed - T is used as a parameter
void SetChild(T child);
}
You can read more about covariance, and its inverse (contravariance) here: Difference between Covariance & Contra-variance

Related

.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.

Compiler Issue with Generics and Inheritance

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.

Abstract class and interface with the same generic method

I'm writing two APIs that I will use with many of my projects. Some projects my use one of the APIs, some the other, but the majority of my projects will use both. I'm trying to design them as if they're completely separate, but I'm struggling on one thing.
namespace FirstApi {
public abstract class MyBaseClass {
//constructor, some methods and properties
public IEnumerable<T> Search<T>() where T : MyBaseClass, new() {
//search logic here. must use generics as I create new instances of T here
}
}
}
namespace SecondApi {
public interface IMyInterface {
//some property and method signatures
IEnumerable<T> Search<T>() where T : IMyInterface, new();
}
}
namespace MyProject {
public class MyDerivedClass : MyBaseClass, IMyInterface {
}
}
Both APIs require this search method. The second API has some functionality in other classes that calls IMyInterface.Search<T>(), and I would like those classes that inherit MyBaseClass to use the Search<T> function defined in MyBaseClass.
Compilation error: The constraints for type parameter 'T' of method 'MyBaseClass.Search()' must match the constraints for type parameter 'T' of interface method 'IMyInterface.Search()'. Consider using an explicit interface implementation instead.
Note: When Search is called, T will always be the derived class of whichever abstract class or interface has been inherited. This was the only way I could find of achieving this in C# 2.0 (C# abstract class return derived type enumerator), and it's just caused more problems!
Is there a type-safe way that I can achieve this, without using objects and casting?
Solution:
Based on the accepted answer by Andras Zoltan, I created this class in my project, and will have to re-create this class for each project that uses both APIs.
public abstract class ApiAdapter<TAdapter> : MyBaseClass, IMyInterface where TAdapter: MyBaseClass, IJsonObject, new()
{
IEnumerable<T> IJsonObject.Search<T>()
{
foreach (TAdapter row in base.Search<TAdapter>())
yield return (T)(IMyInterface)row;
}
}
I then inherit this class like so.
public class Client : ApiAdapter<Client> {
//everything else can go here
}
You can explicitly implement the interfaces Search method, e.g.
public class MyDerivedClass : BasicTestApp.FirstApi.MyBaseClass, BasicTestApp.SecondApi.IMyInterface
{
IEnumerable<T> SecondApi.IMyInterface.Search<T>()
{
// do implementation
}
}
However, I think you are asking for the MyBaseClass Search method to be called when the part of the code that handles your object as IMyInterface calls the Search<T> method. I cannot see a way because you have two T types with different constraints that cannot be related.
If you did where T : BasicTestApp.FirstApi.MyBaseClass, IMyInterface, new(); in both definitions of the Search method then you would not have a problem but this would tie both your APIs together
Here is a possible implementation of your explicitly implemented interface method. It doesn't avoid the cast but at least keeps it neat.
IEnumerable<T> SecondApi.IMyInterface.Search<T>()
{
var results = base.Search<MyDerivedClass>();
return results.Cast<T>();
}
I started my answer with exposition on why it's not working for you, but I think that's well understood now so I'll leave it out.
I've upvoted #IndigoDelta's answer but it highlights something I don't like about the overall design here - I have a sneaking suspicion you should actually be using a generic interface and generic class; not generic methods because it doesn't make any sense that:
Note: When Search is called, T will always be the derived class of whichever abstract class or interface has been inherited.
I'm throwing this solution into the mix; which I think is better because it means that each derived type doesn't need to reimplement the IMyInterface.Search method, and it goes some way to actually enforcing this rule you mention. It's a generic type dedicated to join the two APIs together, meaning the derived types don't need to do anything:
namespace MyProject
{
using FirstApi;
using SecondApi;
public class SecondAPIAdapter<T2> : MyBaseClass, IMyInterface
where T2 : SecondAPIAdapter<T2>, new()
{
#region IMyInterface Members
IEnumerable<T> IMyInterface.Search<T>()
{
return Search<T2>().Cast<T>();
}
#endregion
}
//now you simply derive from the APIAdapter class - passing
//in your derived type as the generic parameter.
public class MyDerivedClass : SecondAPIAdapter<MyDerivedClass>
{ }
}
i think you can do explicit implementation of interface and when you will access methor thru IMyInterface.Search - compiler will run the right method.
You need to use an explicit implementation.
public class MyDerivedClass : MyBaseClass, IMyInterface
{
// The base class implementation of Search inherited
IEnumerable<T> IMyInterface.Search<T>()
{
// The interface implementation
throw new NotImplementedException();
// this would not work because base does not implement IMyInterface
return base.Search<T>();
}
}
Since the implementations are different this makes sense. If they are not different then either the base class should implement the interface and you should use covariance (.Net 4.0 only) to combine your contraints or, perhaps you don't need the interface at all.
I hope I'm not confused, could you not change your definitions, such that:
public interface IMyInterface<in T>
{
//some property and method signatures
IEnumerable<U> Search<U>() where U : T, new();
}
Providing a generic argument of T which can use to enforce that the implementation provides a search function constraint to types of T:
public abstract class MyBaseClass : IMyInterface<MyBaseClass>
{
public virtual IEnumerable<T> Search<T>() where T : MyBaseClass, new()
{
}
}
That way, your derived types are simply:
public class MyDerivedClass : MyBaseClass
{
}
Which you can then do searches as:
var derived = new MyDerivedClass();
IMyInterface<MyDerivedClass> iface = impl;
var results = iface.Search<MyDerivedClass>();

C# Method Overload Problem With Class Derived From Generic Abstract Class

I am working on a project, and I have a generic abstract type that takes a type parameter that is itself derived from the abstract type. If you want to know why I would do this, please see this question.
I have run into an interesting problem with overloading a method in a derived class that is defined in the abstract class. Here is a code sample:
public abstract class AbstractConverter<T, U>
where U : AbstractConvertible
where T : AbstractConverter<T, U>
{
public abstract T Convert(U convertible);
}
public class DerivedConvertibleConverter : AbstractConverter<DerivedConvertibleConverter, DerivedConvertible>
{
public DerivedConvertibleConverter(DerivedConvertible convertible)
{
Convert(convertible);
}
public override DerivedConvertibleConverter Convert(DerivedConvertible convertible)
{
//This will not be called
System.Console.WriteLine("Called the most derived method");
return this;
}
public DerivedConvertibleConverter Convert(Convertible convertible)
{
System.Console.WriteLine("Called the least derived method");
return this;
}
}
public abstract class AbstractConvertible {}
public class Convertible : AbstractConvertible {}
public class DerivedConvertible : Convertible {}
In the sample above, the overload of Convert that does not exist in the abstract parent (and is less derived) is called. I would expect that the most derived version, from the parent class, would be called.
In trying to troubleshoot this problem, I ran into an interesting solution:
public abstract class AbstractConverter<U>
where U : AbstractConvertible
{
public abstract AbstractConverter<U> Convert(U convertible);
}
public class DerivedConvertibleConverter : AbstractConverter<DerivedConvertible>
{
public DerivedConvertibleConverter(DerivedConvertible convertible)
{
Convert(convertible);
}
public override DerivedConvertibleConverter Convert(DerivedConvertible convertible)
{
System.Console.WriteLine("Called the most derived method");
return this;
}
public DerivedConvertibleConverter Convert(Convertible convertible)
{
System.Console.WriteLine("Called the least derived method");
return this;
}
}
public abstract class AbstractConvertible {}
public class Convertible : AbstractConvertible {}
public class DerivedConvertible : Convertible {}
When the derived type argument is removed from the base class, the most derived version of Convert is called. I would not expect this difference, since I would not have expected the interface of the abstract version of Convert to have changed. However, I must be wrong. Can anyone explain why this difference occurs? Thank you very much in advance.
In the sample above, the overload of Convert that does not exist in the abstract parent (and is less derived) is called. I would expect that the most derived version, from the parent class, would be called
Many people have this expectation. However, the behaviour you are observing is correct and by design.
The overload resolution algorithm goes like this. First we make a list of all the possible accessible methods you could be calling. Methods which override virtual methods are considered to be methods of the class which declared them, not the class which overrode them. Then we filter out the ones where the arguments cannot be converted to the formal parameter types. Then we filter out all the methods that are on any type less derived than any type that had an applicable method. Then we determine which method is better than another, if there is still more than one method left.
In your case there are two possible applicable methods. The one that takes a DerivedConvertible is considered to be a method of the base class, and is therefore not as good as the one that takes a Convertible.
The principle here is that overriding a virtual method is an implementation detail subject to change, and not a hint to the compiler that the overriding method is to be chosen.
More generally, these features of the overload resolution algorithm are designed to help mitigate various versions of the Brittle Base Class problem.
For more details about these design decisions see my article on the subject:
http://blogs.msdn.com/b/ericlippert/archive/2007/09/04/future-breaking-changes-part-three.aspx
When the derived type argument is removed from the base class, the most derived version of Convert is called. I would not expect this difference, since I would not have expected the interface of the abstract version of Convert to have changed
The question is based on a false premise; the most derived version is not called. The program fragment is erroneous, and therefore does not compile, and therefore neither method is called; the program doesn't run because it does not compile.

Categories