Generic contraints on derived classes - c#

I have class A:
public class ClassA<T>
Class B derives from A:
public class ClassB : ClassA<ClassB>
Class C derives from class B:
public class ClassC : ClassB
Now I have a generic method with constraints
public static T Method<T>() where T : ClassA<T>
OK, now I want to call:
ClassC c = Method<ClassC>();
but I get the compile error saying:
Type argument 'ClassC' does not inherit from or implement the constraint type 'ClassA<ClassC>.
Yet, the compiler will allow:
ClassB b = Method<ClassB>();
My understanding is that this fails because ClassC inherits ClassA<ClassB> instead of ClassA<ClassC>
My real question is, is it possible to create a class deriving from ClassB that can be used in some way with the generic method?
This may seem like generics are overused and I would agree. I am trying to create business layer objects deriving from the subsonic data objects in a separate project.
Note: I have put the < T > with extra spaces otherwise they get stripped from the question.

Well, you could change Method to:
public static T Method<T,U>() where T : ClassA<U> where U : T
Does that help at all? It's not much use if you can't change Method of course...

No. You must change or wrap this method.
Here is the reason.
ClassC inherits from ClassB which inherits from ClassA(ClassB)
ClassC does not inherit from ClassA(ClassC)
No child of ClassB will inherit from ClassA(child class), because they instead inherit from ClassB and ClassB does not inherit from ClassA(child class).
Generic types are invariant.

In most cases it is possible to solve this scenario by having a base non-generic abstract class:
public abstract class BasicClassA
{
}
public class ClassA<T> : BasicClassA
{
}
public class ClassB : ClassA<ClassB>
{
}
public class ClassC : ClassB
{
}
public static T Method<T>() where T : BasicClassA
{
return null;
}
Your mileage may vary, though.

Related

Cannot change return type when overriding method, error when using generics

I am having trouble overriding a method that returns a generic class that is strongly typed.
This is just setting up for the example below
public class Something : ISomething {}
public interface ISomething {}
My issue is with the Clone method below, only the second one works.
public class ClassA : AbstractClass<Something>
{
public override ClassA Clone() // <--- this doesn't work
{
return this; // this is just dummy code
}
public override AbstractClass<Something> Clone() // <-- this works
{
return this; // this is just dummy code
}
}
public abstract class AbstractClass<T> where T : ISomething
{
public abstract AbstractClass<T> Clone();
}
I would prefer to have the first Clone method, as the name of the class doesn't change. But only the second method compiles. Is there any way to avoid this?
The first override changes the actual signature:
Your method on the base class establishes that it will return an AbstractClass<T>.
Since your subclass says that T is going to be something, this now implies that the method returns an AbstractClass<Something>. While ClassA is an AbstractClass<Something> that doesn't mean that all AbstractClass<Something> are a ClassA.
Consider the following example:
AbstractClass<Something> myVar = new ClassA(); // works, because ClassA is an AbstractClass<Something>
// Now we hold a reference to an AbstractClass<Something>
myVar.Clone(); // => This should return an AbstractClass<Something> not ClassA
Extending the example: consider a ClassB:
public class ClassB : AbstractClass<Something> {}
Both ClassA and ClassB are AbstractClass<Something>. If we retake the first example:
AbstractClass<Something> myVar;
myVar = new ClassA();
myVar = new ClassB();
// Here you don't know whether it's a ClassA or ClassB, you only know it's an `AbstractClass<Something>`
myVar.Clone();
You could add another generic parameter for the return type:
public abstract class AbstractClass<TC, T> where TC: AbstractClass<TC, T> where T : ISomething
{
public abstract TC Clone();
}
then change ClassA to:
public class ClassA : AbstractClass<ClassA, Something>
{
public override ClassA Clone()
{
return this;
}
}
It is impossible to change the return type while overriding a method.
You could remove the override from the first implementation, and this will define a new method. But, because you have that abstract Clone method in the base class, you are forced to implement it as well, and you'll end up with 2 methods with the same name, and the same parameters, so the code will not compile.
Do you really need that Clone method in the abstract class ?
I think you should remove the Clone method from the abstract class, and add the Clone method in the derived class (perhaps also implementing ICloneable interface).

using base class in interface

I have one base class:
class BaseClass{ }
I want to implement interface, that uses base class:
public interface IClass
{
BaseClass GetValue();
}
and then create child classes, that implemented this interface, but in method returned thier own type of class:
class Child: BaseClass, IClass
{
Child GetValue();
}
How can I do that correctly?
Use generics:
public interface IClass<T> where T : BaseClass
{
T GetValue();
}
class Child: BaseClass, IClass<Child>
{
Child GetValue();
}
That's not so easy. The way you would expect it to work doesn't because any class implementing IClass must exactly implement the method from the interface (If you've got an instance of IClass, how would you know that the static type returned by GetValue() is Child? There are workarounds using Generics, but they may be not so nice to use, i.e.
public T GetValue<T>();
where you can exactly specify the type you want to have returned. This, however, may get really ugly if used in combination with virtual overloads. Another alternative is creating a new implementation:
class Child: BaseClass, IClass
{
BaseClass IClass.GetValue();
new Child GetValue();
}
Like this, you get a BaseClass if the static type of the object in the call is IClass and a Child if it is statically a Child.
Remember that
IClass a = new Child();
Child b = a.GetValue(); // Error: Cannot convert BaseClass to Child
will never work, since the static return type of IClass.GetValue() is BaseClass.

Generic base class with multiple children

I currently have a small object hierarchy that looks like this:
public class BaseClass {
// this class is empty and exists only so the others can extend it and share the
// same base type
}
public class ChildA : BaseClass {
public Subject<AssociatedClassA> Results;
}
public class ChildB : BaseClass {
public Subject<AssociatedClassB> Results;
}
In my design I would like to enforce that every class that extends from BaseClass should contain a Subject<SomeType> called Results. I'm wondering if there is a way that I can move Results into the base class or an interface such that I can supply the generic type for the Subject when constructing the base class. For example, it would be awesome if I could do something like this:
ChildA<AssociatedClassA> instance = new ChildA<AssociatedClassA>();
Or even better since there should really only be one template parameter that matches with ChildA if when I constructed it that could be taken care of for me:
ChildA instance = new ChildA();
// Results is automatically set to Subject<AssociatedClassA>
I'm stuck trying to implement this now as if I try to move Results into the base class the Subject requires a template parameter which I can't necessarily supply. There could potentially be more than 2 derived classes and I don't like the idea that someone extending this system has to know to add Results manually to each child class.
Following the suggestions of the 2 answers below this solves my desire to move Results into the base class, however I've run into another issue in that I was hoping to be able to use BaseClass as a generic parameter to methods such that any of the derived classes could be used. For example:
public void ProcessBaseClass(BaseClass base) {
// base could be ChildA or ChildB here
}
This no longer works since BaseClass now requires a type argument. Is there any way that I can have the best of both worlds here or am I stuck due to my design choices?
If appropriate, you can make the parent generic:
public class BaseClass<T> {
public Subject<T> Results;
}
public class ChildA : BaseClass<AssociatedClassA> {
}
public class ChildB : BaseClass<AssociatedClassB> {
}
You can make the base class itself generic:
public class BaseClass<T> {
public T Results { get; protected set; }
}

Cannot use polymorphism because of generic base class

I create base generic class with no fields with just one method
public class Base<T> where T:class
{
public static T Create()
{
// create T somehow
}
}
public class Derived1 : Base<Derived1>
{
}
public class Derived2 : Base<Derived2>
{
}
public class Program
{
bool SomeFunction()
{
// Here I need reference to base class
Base baseref; // error here
switch(somecondition)
{
case 1:
baseref = Derived1.Create();
break;
case 2:
baseref = Derived1.Create();
break
}
// pass baseref somewhere
}
}
An obvious option would be converting base class to interface, but this is not possible because interface cannot contain static methods.
I think I need some intermediate base class. Please suggest
You must remove the generic parameter from the Base class, you can move it to just the Create method:
public class Base
{
public static T Create<T>() where T : class
{
return Activator.CreateInstance<T>();
}
}
public class Derived1 : Base
{
}
public class Derived2 : Base
{
}
Preliminary Assessment
With this statement,
public class Derived1 : Base<Derived1> {
you're using Derived1 in two different ways according to the base class.
You're effectively telling the C# compiler that Derived1 both:
inherits Base
and Base uses instances of Derived1 through non-inheritance means.
This is not wrong (if that's what you really want), but it's unusual for most programming scenarios; you normally choose one or the other. However the benefit of your logic is: not only do you have an implicit instance of Derived1 through inheritance (same for any other derived class), but the base class can also handle other external instances of that same derived type through the type parameter <T>
One problem I see in the Base class is it turns into a kind of circular scenario when using the factory method as intended, because, to support all derived classes it would need to support something like class Base<T> where T:Base<T>. That's next to impossible to declare because you would have to say in a circular fashion: Base<Base<Base<!!!>>> baseref = null; where !!! represents an infinite number of the same.
One Solution...
One possible (and strong solution) is to move the Type parameter from the class to the factory Create method and restrict its usage to the Base class type like so:
using System;
public abstract class Base
{
public static T Create<T>() where T : Base
{
return Activator.CreateInstance<T>();
}
}
Note: I have made the base class abstract which restricts instantiation to the derived types; however you can still use base class references (see switch statement usage below).
These derived classes still inherit from base.
public class Derived1 : Base
{
}
public class Derived2 : Base
{
}
Your factory method is restricted to create only instances of derived types. The logic has been swapped around so the derived type is given to the factory method instead of the factory method being called on it.
public class Program
{
bool SomeFunction()
{
Base baseref = null;
switch(DateTime.Now.Second)
{
case 1:
baseref = Base.Create<Derived1>(); // OK
break;
case 2:
baseref = Base.Create<Derived2>(); //OK
break;
case 60:
baseref = Base.Create<string>(); //COMPILE ERR - good because string is not a derived class
break;
}
// pass baseref somewhere
}
}
public abstract class Base
{
}
public class Base<T> : Base where T : class
{
public static T Create()
{
// create T somehow
}
}
public class Derived1 : Base<Derived1> // also inherits non-generic Base type
{
}
public class Derived2 : Base<Derived2> // also inherits non-generic Base type
{
}
How about creating an interface and having the abstract class implement the interface?

Single Inheritance in C# - object class?

I have been asking myself this question for a long time now. Thought of posting it. C# doesn't support Multiple Inheritance(this is the fact). All classes created in C# derive out of 'Object' class(again a fact).
So if C# does not support Multiple inheritance, then how are we able to extend a class even though it already extends Object class?
Illustating with an example:
class A : object - Class A created.
class B : object - Class B created.
class A : B - this again is supported. What happens to the earlier association to object.
We are able to use object class methods in A after step 3. So is the turned to multi level inheritance. If that is the case, then
class A : B
class C : B
class A : C - I must be able to access class B's methods in A. Which is not the case?
Can anyone please explain?
Joel's answer is correct. There is a difference between multiple inheritance and an inhertance tree (or derivation chain). In your example, you actually show an inhertance tree: One object inherits (derives) from another object higher in the tree. Multiple inheritance allows one object to inherit from multiple base classes.
Take, for example, the following tree:
public class BaseClass { }
public class SpecialBaseClass : BaseClass {}
public class SpecialtyDerivedClass : SpecialBaseClass {}
This is perfectly valid and says that SpecialtyDerivedClass inherits from SpecialBaseClass (SpecialtyDerivedClass' parent) which, in turn, derives from BaseClass (SpecialtyDerivedClass' grandparent).
Under the idea of multiple inheritance, the example would look like this:
public class BaseClass { }
public class SpecialBaseClass {}
public class SpecialtyDerivedClass : BaseClass, SpecialBaseClass {}
This is not allowed in .NET, but it says that SpecialityDerivedClass inherits from both BaseClass and SpecialBaseClass (which are both parents).
.NET does allow a form of multiple inheritance by allowing you to inherit from more than one interface. Changing the example above slightly:
public class BaseClass { }
public interface ISpecialBase {}
public interface ISpecialDerived {}
public class SpecialtyDerivedClass : BaseClass, ISpecialBase, ISpecialDerived {}
This says that SpecialtyDerivedClass inherits from BaseClass (it's parent) and also ISpecialBase and ISpecialDerived (also parent's but more like step-parents as interfaces can't specify functionality).
You're confusing mutliple inheritance with an inheritance tree. You can inherit from something other than Object. It's just that Object is sitting way up there at the top of your tree. And someone can inherit your class, but because Object is still up there at the top that class will also inherit from object. Your "Multi-level" inheritance is not multiple inheritance.
Multiple inheritance is when you inherit from two different trees, and .Net actually does support this after a fashion via interfaces.
All classes ultimately derive from Object.
public class A
is implicitly equivalent to
public class A : System.Object
When you derive from another class
public class A : B
where
public class B : System.Object
B becomes the parent class, and Object becomes the grandparent class.
And so on.
So it is the parent, grandparent, great-grandparent (etc) class of all other classes.
One way to look at it is this: C# has an inheritance tree, while C++ (or other muliple-inheritance languages) has an inheritance lattice.
Given below.
public class A : B
{
}
public class B : C
{
public int BProperty { get; set; }
}
public class C
{
public int CProperty { get; set; }
}
public class Test
{
public void TestStuff()
{
A a = new A();
// These are valid.
a.CProperty = 1;
a.BProperty = 2;
}
}
This is valid. Object is a base for C in this case.
In the example, the reason that B can extend A is because A extends Object. A class can only specify one parent class, but that class must either be object or have object as one of its ancestors.
A class inherits from object if you do not specify a base class. Thus:
class C {}
is the same as
class C : Object {}
However, if you specify a base class, it will inherit from that class instead of Object. Thus,
class B : C {}
B directly inherits from C instead of Object. Another example,
class A : B {}
In this case, A inherits from B instead of Object. To summarize, in this hierarchy:
class C {}
class B : C {}
class A : B {}
Class A derives from B, which derives from C. So Class A is indirectly derived from C because B is derived from C. C also derived from Object which in not explicitly specified but it is there by default. So A is indirectly derived from Object too.
A class in C# can only have one parent, but it can have multiple ancestors. You can implement multiple interfaces, but that only means that your class agrees to implement the signatures defined by those interfaces. You don't actually inherit any functionality from those interfaces.

Categories

Resources