How to Override with Derived Types

Can I Override with derived types?

This is not possible in any .NET language because of type-safety concerns. In type-safe languages, you must provide covariance for return values, and contravariance for parameters. Take this code:

class B {
S Get();
Set(S);
}
class D : B {
T Get();
Set(T);
}

For the Get methods, covariance means that T must either be S or a type derived from S. Otherwise, if you had a reference to an object of type D stored in a variable typed B, when you called B.Get() you wouldn't get an object representable as an S back -- breaking the type system.

For the Set methods, contravariance means that T must either be S or a type that S derives from. Otherwise, if you had a reference to an object of type D stored in a variable typed B, when you called B.Set(X), where X was of type S but not of type T, D::Set(T) would get an object of a type it did not expect.

In C#, there was a conscious decision to disallow changing the type when overloading properties, even when they have only one of the getter/setter pair, because it would otherwise have very inconsistent behavior ("You mean, I can change the type on the one with a getter, but not one with both a getter and setter? Why not?!?" -- Anonymous Alternate Universe Newbie).

C# : Override a property and return a derived type

Maybe you can try generics, something like this:

public class A<T> where T : B
{
public T Details { get; set; }
}

public class Aext : A<Bext>
{
}

public class B
{

}

public class Bext : B
{

}

If you need you can override T Details with Bext Details, it will work fine.

Overriding a member function with derived type in C++

You can only override virtual functions in a way that still makes it safe to use your derived class in place of the base class (e.g. via pointer to the base).

Let's take the following example with the classes you provided:

B b;
A *a = &b;

X x;
a->g(x);

In the last line there is no way to ensure that the caller of g() will pass Y as expected by B because the caller is using the base class' interface which is the whole point of virtual functions and dynamic polymorphism in general.

On the other hand, it is safe for a return type to be more specific in the derived class:

B b;
A *a = &b;
X *x = a->j();

The last statement is still type-safe even if j() actually returns Y*. This C++ feature is called covariant return type.

You can also read more about covariance and contravariance in type systems here.

Overriding base class type with derived type

You cannot do the way you are trying it. i think what you can do is use generics here:

class Fruit
{
}
class abstract RottenFruits<T> : Fruit where T : class
{
public T fruit;
bool isRotten { get; set; }
}

and now you can do it:

class RottenApple : RottenFruits<Apple>
{

}

How to override a property of a base class with an incompatible type?

Class inheritance in TypeScript does not allow derived classes to be broader than base classes. As per the Handbook:

TypeScript enforces that a derived class is always a subtype of its base class.

This means that members of the class that extends the base class that you override are covariant (as derived class is always a subclass of its base or, put simply, is more specific). Consider the following - the override works because "A" is a subtype of a broader union "A" | "B":

class A {
static b : Array<{ c: "A" | "B" }> = []
}

class B extends A {
static b : Array<{ c: "A" }> = [] // OK
}

However, the opposite results in an assignability error because the overridden members are not contravariant:

class C {
static b : Array<{ c: "C" }> = []
}

class D extends C {
static b : Array<{ c: "C" | "D" }> = [] // Type '"D"' is not assignable to type '"C"'
}

The latter example is semantically equivalent to your case: eventName is declared to be a string literal type onKeyDown, meaning any and all extending classes are not allowed broaden it, hence the error.


Your options are limited, however, there is a hacky way to go around that. Suppose you have the following base class E:

class E {
constructor(public e : string) {}
static b : Array<{ c: "E" }> = []
static s : number = 42;
}

First, let's declare the derived class and name it somehow, let it be FB:

class FB extends E {
constructor(public f: number) {
super(f.toString());
}
}

Pretty simple so far, right? Here comes the juicy part:

const F: Omit<typeof FB,"b"> & { 
new (...args:ConstructorParameters<typeof FB>): InstanceType<typeof FB>
b: Array<{ c: "E" | "D" }>
} = FB;

There is a lot to unpack. By assigning the declared derived class to a variable, we create a class expression const F = FB; which enables the static part of the class to be typed via explicit typing of the F variable. Now for the type itself0:

  • Omit<typeof FB, "b"> ensures the compiler knows the static side of FB (and, consequently, the base class E) is present except for the member b which we will be redefining later.
  • new (...args:ConstructorParameters<typeof FB>): InstanceType<typeof FB> reminds the compiler that F is a constructor, whereas args:ConstructorParameters and InstanceType utilities free us to change the base class without the need to update the derived constructor type.
  • b: ... readds the omitted b member to the static side of the derived class while broadening it (note that as class inheritance is not involved, there is no error).

All the above fixes the b member during compile-time, but we still need to make sure the static member is available at runtime with something like this (see MDN on getOwnPropertyDescriptor / defineProperty for details):

const descr = Object.getOwnPropertyDescriptor(E, "b")!;
Object.defineProperty(F, "b", {
...descr,
value: [...descr.value, { c: "D" }]
});

Finally, let's check if everything works as expected:

console.log(
new F(42), // FB
new F(42).e, // string
F.b, // { c: "E" | "D"; }[]
F.s // number
);

// at runtime:
// FB: {
// "e": "42",
// "f": 42
// },
// "42",
// [{ "c": "D" }],
// 42

Playground with examples above | applied to your case


0 Note that we often have to use the typeof FB type query — if we haven't declared the class earlier and opted to shortcut to const F: { ... } = class ..., we would not be able to refer to the class itself when explicitly typing the variable (if we tried, the compiler would complain of a circular reference).

How to properly override function with different arguments in derived class?

  1. Interface doesn't have any fields
  2. Interface has only pure virtual methods
  3. Name initialize of IExecutor indicates some misunderstanding. Looks like it suppose to be called once at the begging during construction time. It should be hidden in step where some factory creates object implementing IExecutor

So basically I'm suspecting you need more something like this:

class IExecutor
{
public:
virutal ~IExecutor() {}
virtual void execute() = 0;
};

struct QEParams {
int a;
int b;
int c;
};

class QEExec: public IExecutor
{
public:
QEExec(int b, int c) ....

void initialie(); // second step init

void execute() override;
};

class CAExec: public SomeOtherBaseClass, public IExecutor
{
public:
CAExec(int a, int c) ....

void execute() override;
};

std::unique_ptr<IExecutor> executorFactory(const QEParams& params)
{
if (params.a < 0) {
auto result = std::make_unique<QEExec>(params.b, params.c);
result->initialie();
return result;
}
return std::make_unique<CAExec>(params.a, params.c);
}

Usually factory parameters are structural data and extra abstraction is obsolete.

If different kind of arguments are needed to create alternative version of IExecutor you just provide different factory function (possibly overload):

std::unique_ptr<IExecutor> executorFactory(const std::string& fileName)
{
....
}


Related Topics



Leave a reply



Submit