L'un des problèmes liés à l'héritage multiple est la distinction entre l'héritage d'interface et l'héritage d'implémentation.
C# dispose déjà d'une implémentation propre de l'héritage d'interface (y compris le choix d'implémentations implicites ou explicites) en utilisant des interfaces pures.
En C++, pour chaque classe que vous spécifiez après les deux points dans la balise class
le type d'héritage que vous obtenez est déterminé par le modificateur d'accès ( private
, protected
ou public
). Avec public
vous obtenez tout le désordre de l'héritage multiple - de multiples interfaces sont mélangées avec de multiples implémentations. Avec l'héritage multiple, les interfaces multiples sont mélangées à des implémentations multiples. private
l'héritage, vous obtenez simplement la mise en œuvre. Un objet de type " class Foo : private Bar
"ne peut jamais être passé à une fonction qui attend un Bar
car c'est comme si le Foo
ne possède en réalité qu'une classe privée Bar
et un champ délégation modèle .
L'héritage d'implémentations multiples pures (qui n'est en fait qu'une délégation automatique) ne pose aucun problème et serait formidable en C#.
En ce qui concerne l'héritage d'interfaces multiples à partir de classes, il existe de nombreuses conceptions possibles pour mettre en œuvre cette fonctionnalité. Chaque langage qui dispose de l'héritage multiple a ses propres règles quant à ce qui se passe lorsqu'une méthode est appelée avec le même nom dans plusieurs classes de base. Certains langages, comme Common Lisp (en particulier le système d'objets CLOS) et Python, disposent d'un protocole de méta-objet dans lequel vous pouvez spécifier la priorité de la classe de base.
Voici une possibilité :
abstract class Gun
{
public void Shoot(object target) {}
public void Shoot() {}
public abstract void Reload();
public void Cock() { Console.Write("Gun cocked."); }
}
class Camera
{
public void Shoot(object subject) {}
public virtual void Reload() {}
public virtual void Focus() {}
}
//this is great for taking pictures of targets!
class PhotoPistol : Gun, Camera
{
public override void Reload() { Console.Write("Gun reloaded."); }
public override void Camera.Reload() { Console.Write("Camera reloaded."); }
public override void Focus() {}
}
var pp = new PhotoPistol();
Gun gun = pp;
Camera camera = pp;
pp.Shoot(); //Gun.Shoot()
pp.Reload(); //writes "Gun reloaded"
camera.Reload(); //writes "Camera reloaded"
pp.Cock(); //writes "Gun cocked."
camera.Cock(); //error: Camera.Cock() not found
((PhotoPistol) camera).Cock(); //writes "Gun cocked."
camera.Shoot(); //error: Camera.Shoot() not found
((PhotoPistol) camera).Shoot();//Gun.Shoot()
pp.Shoot(target); //Gun.Shoot(target)
camera.Shoot(target); //Camera.Shoot(target)
Dans ce cas, seule l'implémentation de la première classe listée est implicitement héritée en cas de conflit. La classe des autres types de base doit être explicitement spécifiée pour obtenir leurs implémentations. Pour rendre le système encore plus sûr, le compilateur peut interdire l'héritage implicite en cas de conflit (les méthodes en conflit nécessiteraient toujours un cast).
De plus, vous pouvez mettre en œuvre l'héritage multiple en C# aujourd'hui avec des opérateurs de conversion implicites :
public class PhotoPistol : Gun /* ,Camera */
{
PhotoPistolCamera camera;
public PhotoPistol() {
camera = new PhotoPistolCamera();
}
public void Focus() { camera.Focus(); }
class PhotoPistolCamera : Camera
{
public override Focus() { }
}
public static Camera implicit operator(PhotoPistol p)
{
return p.camera;
}
}
Elle n'est cependant pas parfaite, car elle n'est pas prise en charge par le programme is
y as
les opérateurs, et System.Type.IsSubClassOf()
.