Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to override a method with a derived parameter instead of a base one?

I'm stuck in this situation where:

  1. I have an abstract class called Ammo, with AmmoBox and Clip as children.
  2. I have an abstract class called Weapon, with Firearm and Melee as children.
  3. Firearm is abstract, with ClipWeapon and ShellWeapon as children.
  4. Inside Firearm, there's a void Reload(Ammo ammo);

The problem is that, a ClipWeapon could use both a Clip and an AmmoBox to reload:

public override void Reload(Ammo ammo)
{
    if (ammo is Clip)
    {
        SwapClips(ammo as Clip);
    }
    else if (ammo is AmmoBox)
    {
        var ammoBox = ammo as AmmoBox;
        // AddBullets returns how many bullets has left from its parameter
        ammoBox.Set(clip.AddBullets(ammoBox.nBullets));
    }
}

But a ShellWeapon, could only use an AmmoBox to reload. I could do this:

public override void Reload(Ammo ammo)
{
    if (ammo is AmmoBox)
    {
        // reload...
    }
}

But this is bad because, even though I'm checking to make sure it's of type AmmoBox, from the outside, it appears like a ShellWeapon could take a Clip as well, since a Clip is Ammo as well.

Or, I could remove Reload from Firearm, and put it both ClipWeapon and ShellWeapon with the specific params I need, but doing so I will lose the benefits of Polymorphism, which is not what I want to.

Wouldn't it be optimal, if I could override Reload inside ShellWeapon like this:

public override void Reload(AmmoBox ammoBox)
{
   // reload ... 
}

Of course I tried it, and it didn't work, I got an error saying the signature must match or something, but shouldn't this be valid 'logically'? since AmmoBox is a Ammo?

How should I get around this? And in general, is my design correct? (Note I was using interfaces IClipWeapon and IShellWeapon but I ran into trouble, so I moved to using classes instead)

Thanks in advance.

like image 421
vexe Avatar asked Jul 30 '13 10:07

vexe


People also ask

Can you override a method with different parameters?

You can have multiple versions of a method with the same name having different number of parameters. You can have all these in the main/parent class or you can add the newer versions in the subclasses only. There is no restriction on doing this.

How do you override a derived class method?

In C#, a method in a derived class can have the same name as a method in the base class. You can specify how the methods interact by using the new and override keywords. The override modifier extends the base class virtual method, and the new modifier hides an accessible base class method.

Can you override inherited methods?

To override an inherited method, the method in the child class must have the same name, parameter list, and return type (or a subclass of the return type) as the parent method. Any method that is called must be defined within its own class or its superclass.

Why would override a method of a base class?

Method overriding, in object-oriented programming, is a language feature that allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its superclasses or parent classes.


2 Answers

but shouldn't this be valid 'logically'?

No. Your interface says that the caller can pass in any Ammo - where you're restricting it to require an AmmoBox, which is more specific.

What would you expect to happen if someone were to write:

Firearm firearm = new ShellWeapon();
firearm.Reload(new Ammo());

? That should be entirely valid code - so do you want it to blow up at execution time? Half the point of static typing is to avoid that sort of problem.

You could make Firearm generic in the type of ammo is uses:

public abstract class Firearm<TAmmo> : Weapon where TAmmo : Ammo
{
    public abstract void Reload(TAmmo ammo);
}

Then:

public class ShellWeapon : Firearm<AmmoBox>

That may or may not be a useful way of doing things, but it's at least worth considering.

like image 124
Jon Skeet Avatar answered Oct 10 '22 01:10

Jon Skeet


You can use composition with interface extensions instead of multiple-inheritance:

class Ammo {}
class Clip : Ammo {}
class AmmoBox : Ammo {}

class Firearm {}
interface IClipReloadable {}
interface IAmmoBoxReloadable {}

class ClipWeapon : Firearm, IClipReloadable, IAmmoBoxReloadable {}
class AmmoBoxWeapon : Firearm, IAmmoBoxReloadable {}

static class IClipReloadExtension {
    public static void Reload(this IClipReloadable firearm, Clip ammo) {}
}

static class IAmmoBoxReloadExtension {
    public static void Reload(this IAmmoBoxReloadable firearm, AmmoBox ammo) {}
}

So that you will have 2 definitions of Reload() method with Clip and AmmoBox as arguments in ClipWeapon and only 1 Reload() method in AmmoBoxWeapon class with AmmoBox argument.

var ammoBox = new AmmoBox();
var clip = new Clip();

var clipWeapon = new ClipWeapon();
clipWeapon.Reload(ammoBox);
clipWeapon.Reload(clip);

var ammoBoxWeapon = new AmmoBoxWeapon();
ammoBoxWeapon.Reload(ammoBox);

And if you try pass Clip to AmmoBoxWeapon.Reload you will get an error:

ammoBoxWeapon.Reload(clip); // <- ERROR at compile time
like image 44
Mikhail Churbanov Avatar answered Oct 10 '22 01:10

Mikhail Churbanov