Zoeken…


Syntaxis

  • klasse DerivedClass: BaseClass
  • class DerivedClass: BaseClass, IExampleInterface
  • class DerivedClass: BaseClass, IExampleInterface, IAnotherInterface

Opmerkingen

Klassen kunnen rechtstreeks van slechts één klasse erven, maar kunnen (in plaats van of tegelijkertijd) een of meer interfaces implementeren.

Structs kunnen interfaces implementeren, maar kunnen van geen enkel type expliciet erven. Ze nemen impliciet over van System.ValueType , dat op zijn beurt rechtstreeks van System.Object System.ValueType .

Statische klassen kunnen geen interfaces implementeren.

Overerving van een basisklasse

Om dubbele code te voorkomen, definieert u algemene methoden en attributen in een algemene klasse als basis:

public class Animal 
{
    public string Name { get; set; }
    // Methods and attributes common to all animals
    public void Eat(Object dinner)
    {
        // ...
    }
    public void Stare()
    {
        // ...
    }
    public void Roll()
    {
        // ...
    }
}

Nu je een klasse hebt die Animal in het algemeen vertegenwoordigt, kun je een klasse definiëren die de eigenaardigheden van specifieke dieren beschrijft:

public class Cat : Animal
{
    public Cat() 
    {
        Name = "Cat";
    }
    // Methods for scratching furniture and ignoring owner
    public void Scratch(Object furniture)
    {
        // ...
    }
}

De Cat-klasse krijgt niet alleen toegang tot de methoden die in de definitie expliciet zijn beschreven, maar ook tot alle methoden die zijn gedefinieerd in de algemene basisklasse voor Animal . Elk dier (ongeacht of het een kat was) kon eten, staren of rollen. Een dier zou echter niet kunnen krabben, tenzij het ook een kat was. Je zou dan andere klassen kunnen definiëren die andere dieren beschrijven. (Zoals Gopher met een methode om bloementuinen te vernietigen en Luiaard zonder extra methoden.)

Overnemen van een klasse en een interface implementeren

public class Animal 
{
    public string Name { get; set; }
}

public interface INoiseMaker
{
    string MakeNoise();
}

//Note that in C#, the base class name must come before the interface names
public class Cat : Animal, INoiseMaker
{
    public Cat() 
    {
        Name = "Cat";
    }

    public string MakeNoise()
    {
        return "Nyan";
    }
}

Overnemen van een klasse en meerdere interfaces implementeren

public class LivingBeing
{
    string Name { get; set; }
}

public interface IAnimal 
{
    bool HasHair { get; set; }
}

public interface INoiseMaker
{
    string MakeNoise();
}

//Note that in C#, the base class name must come before the interface names
public class Cat : LivingBeing, IAnimal, INoiseMaker
{
    public Cat() 
    {
        Name = "Cat";
        HasHair = true;
    }

    public bool HasHair { get; set; }

    public string Name { get; set; }

    public string MakeNoise()
    {
        return "Nyan";
    }
}

Overerving testen en navigeren

interface BaseInterface {}
class BaseClass : BaseInterface {}

interface DerivedInterface {}
class DerivedClass : BaseClass, DerivedInterface {}

var baseInterfaceType = typeof(BaseInterface);
var derivedInterfaceType = typeof(DerivedInterface);
var baseType = typeof(BaseClass);
var derivedType = typeof(DerivedClass);

var baseInstance = new BaseClass();
var derivedInstance = new DerivedClass();  

Console.WriteLine(derivedInstance is DerivedClass); //True
Console.WriteLine(derivedInstance is DerivedInterface); //True
Console.WriteLine(derivedInstance is BaseClass); //True
Console.WriteLine(derivedInstance is BaseInterface); //True
Console.WriteLine(derivedInstance is object); //True

Console.WriteLine(derivedType.BaseType.Name);  //BaseClass
Console.WriteLine(baseType.BaseType.Name);  //Object
Console.WriteLine(typeof(object).BaseType);  //null

Console.WriteLine(baseType.IsInstanceOfType(derivedInstance));  //True
Console.WriteLine(derivedType.IsInstanceOfType(baseInstance));  //False

Console.WriteLine(
    string.Join(",", 
    derivedType.GetInterfaces().Select(t => t.Name).ToArray()));
//BaseInterface,DerivedInterface
    
Console.WriteLine(baseInterfaceType.IsAssignableFrom(derivedType)); //True
Console.WriteLine(derivedInterfaceType.IsAssignableFrom(derivedType)); //True
Console.WriteLine(derivedInterfaceType.IsAssignableFrom(baseType)); //False

Een abstracte basisklasse uitbreiden

In tegenstelling tot interfaces, die kunnen worden omschreven als contracten voor implementatie, fungeren abstracte klassen als contracten voor verlenging.

Een abstracte klasse kan niet worden geïnstantieerd, deze moet worden uitgebreid en de resulterende klasse (of afgeleide klasse) kan vervolgens worden geïnstantieerd.

Abstracte klassen worden gebruikt om generieke implementaties te bieden

public abstract class Car
{
    public void HonkHorn() {
        // Implementation of horn being honked
    }
}

public class Mustang : Car
{
    // Simply by extending the abstract class Car, the Mustang can HonkHorn()
    // If Car were an interface, the HonkHorn method would need to be included
    // in every class that implemented it.
}

Het bovenstaande voorbeeld laat zien hoe elke klasse die Auto uitbreidt automatisch de HonkHorn-methode ontvangt met de implementatie. Dit betekent dat elke ontwikkelaar die een nieuwe auto maakt, zich geen zorgen hoeft te maken over hoe hij toetert.

Aannemers in een subklasse

Wanneer u een subklasse van een basisklasse maakt, kunt u de basisklasse samenstellen met : base achter de parameters van de subklasse constructor.

class Instrument
{
    string type;
    bool clean;

    public Instrument (string type, bool clean)
    {
        this.type = type;
        this.clean = clean;
    }
}

class Trumpet : Instrument
{
    bool oiled;

    public Trumpet(string type, bool clean, bool oiled) : base(type, clean)
    {
        this.oiled = oiled;
    }
}

Erfenis. Oproepen van constructeurs

Overweeg dat we een klasse Animal die een kindklasse Dog

class Animal
{
    public Animal()
    {
        Console.WriteLine("In Animal's constructor");
    }
}

class Dog : Animal
{
    public Dog()
    {
        Console.WriteLine("In Dog's constructor");
    }
}

Standaard neemt elke klasse impliciet de klasse Object .

Dit is hetzelfde als de bovenstaande code.

class Animal : Object
{
    public Animal()
    {
        Console.WriteLine("In Animal's constructor");
    }
}

Wanneer u een instantie van Dog klasse Dog , wordt de standaardconstructor van de basisklassen (zonder parameters) aangeroepen als er geen expliciete aanroep is naar een andere constructor in de bovenliggende klasse . In ons geval wordt deze eerst Object's constructor van Object's genoemd, vervolgens Object's constructor van Animal's en uiteindelijk Dog's constructor van Dog's .

public class Program
{
    public static void Main()
    {
        Dog dog = new Dog();
    }
}

Uitgang zal zijn

In de constructor van Animal
In de aannemer van Dog

Demo bekijken

Roep de constructor van de ouder expliciet op.

In de bovenstaande voorbeelden noemt onze constructeur Dog klasse de standaardconstructor van de klasse Animal . Als u wilt, kunt u opgeven welke constructor moet worden aangeroepen: het is mogelijk om elke constructor aan te roepen die in de bovenliggende klasse is gedefinieerd.

Overweeg dat we deze twee klassen hebben.

class Animal
{
    protected string name;

    public Animal()
    {
        Console.WriteLine("Animal's default constructor");
    }    

    public Animal(string name)
    {
        this.name = name;
        Console.WriteLine("Animal's constructor with 1 parameter");
        Console.WriteLine(this.name);
    }
}

class Dog : Animal
{
    public Dog() : base()
    {
        Console.WriteLine("Dog's default constructor");
    }  

    public Dog(string name) : base(name)
    {
        Console.WriteLine("Dog's constructor with 1 parameter");
        Console.WriteLine(this.name);
    }
}

Wat is hier aan de hand?

We hebben 2 constructeurs in elke klasse.

Wat betekent base ?

base is een verwijzing naar de ouderklasse. In ons geval, wanneer we een instantie van de Dog klasse zoals deze maken

Dog dog = new Dog();

De runtime roept eerst Dog() , wat de parameterloze constructor is. Maar zijn lichaam werkt niet onmiddellijk. Na de haakjes van de constructor hebben we een dergelijke aanroep: base() , wat betekent dat wanneer we de standaard Dog constructor aanroepen, deze op zijn beurt de standaardconstructor van de ouder aanroept . Nadat de constructor van de ouder is uitgevoerd, keert deze terug en voert u ten slotte het constructorlichaam Dog() .

Dus de output zal er zo uitzien:

De standaardconstructor van Animal
Standaardconstructeur van de hond

Demo bekijken

Wat nu als we Dog's constructor van de Dog's met een parameter noemen?

Dog dog = new Dog("Rex");

Je weet dat de leden in de bovenliggende klasse die niet privé worden overgenomen door het kind klasse zijn, wat betekent dat Dog zal ook de name het veld.
In dit geval hebben we een argument aan onze constructeur doorgegeven. Het op zijn beurt gaat het argument constructor de ouder klasse met een parameter, die de initialiseert name veld.

Uitgang zal zijn

Animal's constructor with 1 parameter
Rex
Dog's constructor with 1 parameter
Rex

Samenvatting:

Elke creatie van objecten begint bij de basisklasse. In de overerving zijn de klassen in de hiërarchie aan elkaar geketend. Zoals alle klassen ontlenen Object , de eerste constructeur genoemd te worden wanneer een object wordt gemaakt is de Object klasse constructor; Vervolgens wordt de volgende constructor in de keten aangeroepen en pas nadat ze allemaal zijn genoemd, wordt het object gemaakt

basis trefwoord

  1. Het basiszoekwoord wordt gebruikt om toegang te krijgen tot leden van de basisklasse vanuit een afgeleide klasse:
  2. Roep een methode op de basisklasse aan die door een andere methode is overschreven. Geef op welke base-class constructor moet worden aangeroepen bij het maken van instanties van de afgeleide klasse.

Overerving methoden

Er zijn verschillende manieren waarop methoden kunnen worden geërfd

public abstract class Car
{
    public void HonkHorn() {
        // Implementation of horn being honked
    }

    // virtual methods CAN be overridden in derived classes
    public virtual void ChangeGear() {
        // Implementation of gears being changed
    }

    // abstract methods MUST be overridden in derived classes
    public abstract void Accelerate();
}

public class Mustang : Car
{
    // Before any code is added to the Mustang class, it already contains 
    // implementations of HonkHorn and ChangeGear.

    // In order to compile, it must be given an implementation of Accelerate,
    // this is done using the override keyword
    public override void Accelerate() {
        // Implementation of Mustang accelerating
    }

    // If the Mustang changes gears differently to the implementation in Car
    // this can be overridden using the same override keyword as above
    public override void ChangeGear() {
        // Implementation of Mustang changing gears
    }
}

Overerving Antipatronen

Ongepaste erfenis

Laten we zeggen dat er 2 klassen klasse Foo en Bar . Foo heeft twee functies Do1 en Do2 . Bar moet Do1 van Foo , maar het heeft geen Do2 nodig of heeft een functie nodig die gelijk is aan Do2 maar iets totaal anders doet.

Slechte manier : maak Do2() op Foo virtueel en overschrijf het in Bar of throw Exception gewoon in Bar voor Do2()

public class Bar : Foo
{
    public override void Do2()
    {
        //Does something completely different that you would expect Foo to do
        //or simply throws new Exception 
    }
}

Goede manier

Haal Do1() uit Foo en plaats het in de nieuwe klasse Baz , Do1() vervolgens zowel Foo als Bar van Baz en implementeer Do2() afzonderlijk

public class Baz
{
    public void Do1()
    {
        // magic
    }
}

public class Foo : Baz
{
    public void Do2()
    {
        // foo way
    }
}

public class Bar : Baz
{
    public void Do2()
    {
        // bar way or not have Do2 at all
    }
}

Waarom is het eerste voorbeeld slecht en het tweede is goed: wanneer ontwikkelaar nr2 een wijziging in Foo moet doorvoeren, is de kans groot dat hij de implementatie van Bar verbreekt, omdat Bar nu onlosmakelijk met Foo is verbonden. Bij het laatstgenoemde voorbeeld is het Foo en Bar burgerschap verplaatst naar Baz en hebben ze geen invloed op elkaar (zoals dat niet zou moeten).

Basisklasse met recursieve typespecificatie

Eenmalige definitie van een generieke basisklasse met recursieve typespecificatie. Elk knooppunt heeft één ouder en meerdere kinderen.

/// <summary>
/// Generic base class for a tree structure
/// </summary>
/// <typeparam name="T">The node type of the tree</typeparam>
public abstract class Tree<T> where T : Tree<T>
{
    /// <summary>
    /// Constructor sets the parent node and adds this node to the parent's child nodes
    /// </summary>
    /// <param name="parent">The parent node or null if a root</param>
    protected Tree(T parent)
    {
        this.Parent=parent;
        this.Children=new List<T>();
        if(parent!=null)
        {
            parent.Children.Add(this as T);
        }
    }
    public T Parent { get; private set; }
    public List<T> Children { get; private set; }
    public bool IsRoot { get { return Parent==null; } }
    public bool IsLeaf { get { return Children.Count==0; } }
    /// <summary>
    /// Returns the number of hops to the root object
    /// </summary>
    public int Level { get { return IsRoot ? 0 : Parent.Level+1; } }
}

Het bovenstaande kan opnieuw worden gebruikt telkens wanneer een boomhiërarchie van objecten moet worden gedefinieerd. Het knooppuntobject in de boom moet erven van de basisklasse met

public class MyNode : Tree<MyNode>
{
    // stuff
}

elke knooppuntklasse weet waar deze zich bevindt in de hiërarchie, wat het bovenliggende object is en wat de onderliggende objecten zijn. Verschillende ingebouwde types gebruiken een boomstructuur, zoals Control of XmlElement en de bovenstaande Tree<T> kan worden gebruikt als een basisklasse van elk type in uw code.


Als u bijvoorbeeld een hiërarchie van onderdelen wilt maken waarbij het totale gewicht wordt berekend op basis van het gewicht van alle kinderen, doet u het volgende:

public class Part : Tree<Part>
{
    public static readonly Part Empty = new Part(null) { Weight=0 };
    public Part(Part parent) : base(parent) { }
    public Part Add(float weight)
    {
        return new Part(this) { Weight=weight };
    }
    public float Weight { get; set; }

    public float TotalWeight { get { return Weight+Children.Sum((part) => part.TotalWeight); } }
}

te gebruiken als

// [Q:2.5] -- [P:4.2] -- [R:0.4]
//    \
//      - [Z:0.8]
var Q = Part.Empty.Add(2.5f);
var P = Q.Add(4.2f);
var R = P.Add(0.4f);
var Z = Q.Add(0.9f);

// 2.5+(4.2+0.4)+0.9 = 8.0
float weight = Q.TotalWeight;

Een ander voorbeeld zou zijn bij de definitie van relatieve coördinaatframes. In dit geval hangt de ware positie van het coördinatenframe af van de posities van alle bovenliggende coördinatenframes.

public class RelativeCoordinate : Tree<RelativeCoordinate>
{
    public static readonly RelativeCoordinate Start = new RelativeCoordinate(null, PointF.Empty) { };
    public RelativeCoordinate(RelativeCoordinate parent, PointF local_position)
        : base(parent)
    {
        this.LocalPosition=local_position;
    }
    public PointF LocalPosition { get; set; }
    public PointF GlobalPosition
    {
        get
        {
            if(IsRoot) return LocalPosition;
            var parent_pos = Parent.GlobalPosition;
            return new PointF(parent_pos.X+LocalPosition.X, parent_pos.Y+LocalPosition.Y);
        }
    }
    public float TotalDistance
    {
        get
        {
            float dist = (float)Math.Sqrt(LocalPosition.X*LocalPosition.X+LocalPosition.Y*LocalPosition.Y);
            return IsRoot ? dist : Parent.TotalDistance+dist;
        }
    }
    public RelativeCoordinate Add(PointF local_position)
    {
        return new RelativeCoordinate(this, local_position);
    }
    public RelativeCoordinate Add(float x, float y)
    {
        return Add(new PointF(x, y));
    }
}

te gebruiken als

// Define the following coordinate system hierarchy
//
// o--> [A1] --+--> [B1] -----> [C1]
//             |     
//             +--> [B2] --+--> [C2]
//                         |
//                         +--> [C3]

var A1 = RelativeCoordinate.Start;
var B1 = A1.Add(100, 20);
var B2 = A1.Add(160, 10);

var C1 = B1.Add(120, -40);
var C2 = B2.Add(80, -20);
var C3 = B2.Add(60, -30);

double dist1 = C1.TotalDistance;


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow