Vererbung in C# und .NETInheritance in C# and .NET

Dieses Tutorial macht Sie mit der Vererbung in C# vertraut.This tutorial introduces you to inheritance in C#. Vererbung ist eine Funktion der objektorientierten Programmiersprachen, die Ihnen ermöglicht, eine Basisklasse zu definieren, die eine bestimmte Funktionalität bietet (Daten und Verhalten), und abgeleitete Klassen zu definieren, die diese Funktionalität entweder übernehmen oder außer Kraft setzen.Inheritance is a feature of object-oriented programming languages that allows you to define a base class that provides specific functionality (data and behavior) and to define derived classes that either inherit or override that functionality.

Erforderliche KomponentenPrerequisites

In diesem Tutorial wird vorausgesetzt, dass Sie .NET Core installiert haben.This tutorial assumes that you've installed .NET Core. Installationsanweisungen finden Sie im .NET Core-Installationshandbuch.For installation instructions, see .NET Core installation guide. Sie benötigen auch einen Code-Editor.You also need a code editor. In diesem Tutorial wird Visual Studio Code verwendet, obwohl Sie einen Code-Editor Ihrer Wahl verwenden können.This tutorial uses Visual Studio Code, although you can use any code editor of your choice.

Ausführen der BeispieleRunning the examples

Verwenden Sie zum Erstellen und Ausführen der Beispiele in diesem Tutorial das Befehlszeilenhilfsprogramm dotnet.To create and run the examples in this tutorial, you use the dotnet utility from the command line. Gehen Sie für jedes Beispiel wie folgt vor:Follow these steps for each example:

  1. Erstellen Sie ein Verzeichnis zum Speichern des Beispiels.Create a directory to store the example.
  2. Geben Sie den Befehl dotnet new console in einer Befehlszeile ein, um ein neues .NET Core-Projekt zu erstellen.Enter the dotnet new console command at a command prompt to create a new .NET Core project.
  3. Kopieren Sie den Code aus dem Beispiel, und fügen Sie ihn in den Code-Editor ein.Copy and paste the code from the example into your code editor.
  4. Geben Sie den Befehl dotnet restore in der Befehlszeile ein, um die Abhängigkeiten des Projekts zu laden oder wiederherzustellen.Enter the dotnet restore command from the command line to load or restore the project's dependencies.

Hinweis

Starting with .NET Core 2.0, you don't have to run dotnet restore because it's run implicitly by all commands, such as dotnet build and dotnet run, that require a restore to occur. It's still a valid command in certain scenarios where doing an explicit restore makes sense, such as continuous integration builds in Visual Studio Team Services or in build systems that need to explicitly control the time at which the restore occurs.

  1. Geben Sie den Befehl dotnet run zum Kompilieren und Ausführen des Beispiels ein.Enter the dotnet run command to compile and execute the example.

Hintergrund: Was ist Vererbung?Background: What is inheritance?

Vererbung ist eines der wichtigsten Attribute bei der objektorientierten Programmierung.Inheritance is one of the fundamental attributes of object-oriented programming. Sie können damit eine untergeordnete Klasse definieren, die das Verhalten einer übergeordneten Klasse wiederverwendet (erbt), erweitert oder ändert.It allows you to define a child class that reuses (inherits), extends, or modifies the behavior of a parent class. Die Klasse, deren Member geerbt werden, ist die Basisklasse.The class whose members are inherited is called the base class. Die Klasse, die die Member der Basisklasse erbt, ist die abgeleitete Klasse.The class that inherits the members of the base class is called the derived class.

C# und .NET unterstützen nur die einzelne Vererbung.C# and .NET support single inheritance only. D.h., eine Klasse kann nur von einer einzelnen Klasse erben.That is, a class can only inherit from a single class. Allerdings ist Vererbung transitiv, sodass Sie eine Vererbungshierarchie für einen Satz von Typen definieren können.However, inheritance is transitive, which allows you to define an inheritance hierarchy for a set of types. Mit anderen Worten: Typ D kann von Typ C erben, der von Typ B erbt, der vom Basisklassentyp A erbt.In other words, type D can inherit from type C, which inherits from type B, which inherits from the base class type A. Da Vererbung transitiv ist, stehen die Member des Typs A Typ D zur Verfügung.Because inheritance is transitive, the members of type A are available to type D.

Nicht alle Member einer Basisklasse werden von abgeleiteten Klassen geerbt.Not all members of a base class are inherited by derived classes. Die folgenden Member werden nicht geerbt:The following members are not inherited:

  • Statische Konstruktoren, die die statischen Daten einer Klasse initialisieren.Static constructors, which initialize the static data of a class.

  • Instanzkonstruktoren, die Sie aufrufen, um eine neue Instanz der Klasse zu erstellen.Instance constructors, which you call to create a new instance of the class. Jede Klasse muss ihre eigenen Konstruktoren definieren.Each class must define its own constructors.

  • Finalizer, die vom Garbage Collector der Laufzeit aufgerufen werden, um Instanzen einer Klasse zu zerstören.Finalizers, which are called by the runtime's garbage collector to destroy instances of a class.

Während alle anderen Member einer Basisklasse von abgeleiteten Klassen geerbt werden, hängt ihre Sichtbarkeit davon ab, ob auf sie zugegriffen werden kann.While all other members of a base class are inherited by derived classes, whether they are visible or not depends on their accessibility. Ob auf einen Member zugegriffen werden kann, beeinflusst dessen Sichtbarkeit für abgeleitete Klassen wie folgt:A member's accessibility affects its visibility for derived classes as follows:

  • Private Member sind nur in abgeleiteten Klassen sichtbar, die in ihrer Basisklasse geschachtelt sind.Private members are visible only in derived classes that are nested in their base class. Andernfalls sind sie in abgeleiteten Klassen nicht sichtbar.Otherwise, they are not visible in derived classes. Im folgenden Beispiel ist A.B eine geschachtelte Klasse, die sich von A ableitet, und C leitet sich von A ab.In the following example, A.B is a nested class that derives from A, and C derives from A. Das private Feld A.value ist in A.B sichtbar.The private A.value field is visible in A.B. Wenn Sie jedoch die Kommentare aus der C.GetValue-Methode entfernen und versuchen, das Beispiel zu kompilieren, verursacht dies Compilerfehler CS0122: „Der Zugriff auf "A.value" ist aufgrund des Schutzgrads nicht möglich.“However, if you remove the comments from the C.GetValue method and attempt to compile the example, it produces compiler error CS0122: "'A.value' is inaccessible due to its protection level."

    using System;
    
    public class A 
    {
       private int value = 10;
    
       public class B : A
       {
           public int GetValue()
           {
               return this.value;
           }     
       }
    }
    
    public class C : A
    {
    //    public int GetValue()
    //    {
    //        return this.value;
    //    }
    }
    
    public class Example
    {
        public static void Main(string[] args)
        {
            var b = new A.B();
            Console.WriteLine(b.GetValue());
        }
    }
    // The example displays the following output:
    //       10
    
  • Geschützte Member sind nur in abgeleiteten Klassen sichtbar.Protected members are visible only in derived classes.

  • Interne Member sind nur in abgeleiteten Klassen sichtbar, die sich in der gleichen Assembly wie die Basisklasse befinden.Internal members are visible only in derived classes that are located in the same assembly as the base class. Sie sind nicht in abgeleiteten Klassen sichtbar, die sich in einer anderen Assembly als die Basisklasse befinden.They are not visible in derived classes located in a different assembly from the base class.

  • Öffentliche Datenmember in abgeleiteten Klassen angezeigt werden, und sind Teil der öffentlichen Schnittstelle der abgeleiteten Klasse.Public members are visible in derived classes and are part of the derived class' public interface. Öffentlich geerbte Member können so aufgerufen werden, als ob sie in der abgeleiteten Klasse definiert wurden.Public inherited members can be called just as if they were defined in the derived class. Im folgenden Beispiel definiert Klasse A eine Methode namens Method1, und Klasse B erbt von Klasse A.In the following example, class A defines a method named Method1, and class B inherits from class A. Das Beispiel ruft dann Method1 auf, als wäre sie eine Instanzmethode von B.The example then calls Method1 as if it were an instance method on B.

public class A
{
    public void Method1()
    {
        // Method implementation.
    }
}

public class B : A
{ }


public class Example
{
    public static void Main()
    {
        B b = new B();
        b.Method1();
    }
}

Abgeleitete Klassen können auch geerbte Member überschreiben, indem sie eine alternative Implementierung bereitstellen.Derived classes can also override inherited members by providing an alternate implementation. Um einen Member überschreiben zu können, muss der Member in der Basisklasse mit dem Schlüsselwort virtual markiert sein.In order to be able to override a member, the member in the base class must be marked with the virtual keyword. Standardmäßig sind Member der Basisklasse nicht als virtual markiert und können nicht überschrieben werden.By default, base class members are not marked as virtual and cannot be overridden. Der Versuch, wie im folgenden Beispiel einen nicht virtuellen Member zu überschreiben, verursacht den Compilerfehler CS0506: „"" : Der geerbte Member "" kann nicht überschrieben werden, da er nicht als "virtual", "abstract" oder "override" markiert ist.“Attempting to override a non-virtual member, as the following example does, generates compiler error CS0506: " cannot override inherited member because it is not marked virtual, abstract, or override.

public class A
{
    public void Method1()
    {
        // Do something.
    }
}

public class B : A
{
    public override void Method1() // Generates CS0506.
    {
        // Do something else.
    }
}

In einigen Fällen muss eine abgeleitete Klasse die Basisklassenimplementierung überschreiben.In some cases, a derived class must override the base class implementation. Basisklassenmember, die mit dem Schlüsselwort abstract markiert sind, erfordern, dass abgeleitete Klassen sie überschreiben.Base class members marked with the abstract keyword require that derived classes override them. Der Versuch, das folgende Beispiel zu kompilieren, verursacht den Compilerfehler CS0534: „ implementiert den geerbten abstrakten Member nicht.“, da Klasse B keine Implementierung für A.Method1 bietet.Attempting to compile the following example generates compiler error CS0534, " does not implement inherited abstract member ', because class B provides no implementation for A.Method1.

public abstract class A
{
    public abstract void Method1();
}

public class B : A // Generates CS0534.
{
    public void Method3()
    {
        // Do something.
    }
}

Vererbung gilt nur für Klassen und Schnittstellen.Inheritance applies only to classes and interfaces. Andere Typkategorien (Strukturen, Delegate und Enumerationen) unterstützen keine Vererbung.Other type categories (structs, delegates, and enums) do not support inheritance. Aus diesem Grund verursacht der Versuch, Code wie den folgenden zu kompilieren, den Compilerfehler CS0527: „Der Typ "ValueType" in der Schnittstellenliste ist keine Schnittstelle.“Because of this, attempting to compile code like the following produces compiler error CS0527: "Type 'ValueType' in interface list is not an interface." Die Fehlermeldung gibt an, dass die Vererbung nicht unterstützt wird, obwohl Sie die Schnittstellen definieren können, die eine Struktur implementiert.The error message indicates that, although you can define the interfaces that a struct implements, inheritance is not supported.

using System;

public struct ValueStructure : ValueType // Generates CS0527.
{
}

Implizite VererbungImplicit inheritance

Neben Typen, die sie vielleicht über die einzelne Vererbung erben, erben alle Typen im Typensystem von .NET implizit von Object oder einem davon abgeleiteten Typ.Besides any types that they may inherit from through single inheritance, all types in the .NET type system implicitly inherit from Object or a type derived from it. Dadurch wird sichergestellt, dass die allgemeine Funktionalität für einen beliebigen Typ verfügbar ist.This ensures that common functionality is available to any type.

Um zu sehen, was implizite Vererbung bedeutet, definieren wir eine neue Klasse SimpleClass, die einfach eine leere Klassendefinition ist:To see what implicit inheritance means, let's define a new class, SimpleClass, that is simply an empty class definition:

public class SimpleClass
{}

Wir können dann die Reflektion (die uns ermöglicht, die Metadaten eines Typs zu überprüfen, um Informationen zu diesem Typ zu erhalten) verwenden, um eine Liste der Member abzurufen, die zum SimpleClass-Typ gehören.We can then use reflection (which lets us inspect a type's metadata to get information about that type) to get a list of the members that belong to the SimpleClass type. Obwohl wir keine Member in unserer SimpleClass-Klasse definiert haben, gibt die Ausgabe des Beispiels an, dass sie tatsächlich neun Member hat.Although we haven't defined any members in our SimpleClass class, output from the example indicates that it actually has nine members. Einer davon ist ein parameterloser (oder standardmäßiger) Konstruktor, der automatisch vom C#-Compiler für den SimpleClass-Typ angegeben wird.One of these is a parameterless (or default) constructor that is automatically supplied for the SimpleClass type by the C# compiler. Die verbleibenden acht sind Member von Object, dem Typ, von dem alle Klassen und Schnittstellen im .NET-Typsystem letztlich implizit erben.The remaining eight are members of Object, the type from which all classes and interfaces in the .NET type system ultimately implicitly inherit.

using System;
using System.Reflection;

public class Example
{
   public static void Main()
   {
      Type t = typeof(SimpleClass);
      BindingFlags flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | 
                           BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
      MemberInfo[] members = t.GetMembers(flags);
      Console.WriteLine($"Type {t.Name} has {members.Length} members: ");
      foreach (var member in members) {
         string access = "";
         string stat = ""; 
         var method = member as MethodBase;
         if (method != null) {
            if (method.IsPublic) 
               access = " Public";
            else if (method.IsPrivate)
               access = " Private";
            else if (method.IsFamily)  
               access = " Protected";
            else if (method.IsAssembly)
               access = " Internal";
            else if (method.IsFamilyOrAssembly)
               access = " Protected Internal ";
            if (method.IsStatic)
               stat = " Static";
         }
         var output = $"{member.Name} ({member.MemberType}): {access}{stat}, Declared by {member.DeclaringType}";
         Console.WriteLine(output); 

      }
   }
}
// The example displays the following output:
//	Type SimpleClass has 9 members:
//	ToString (Method):  Public, Declared by System.Object
//	Equals (Method):  Public, Declared by System.Object
//	Equals (Method):  Public Static, Declared by System.Object
//	ReferenceEquals (Method):  Public Static, Declared by System.Object
//	GetHashCode (Method):  Public, Declared by System.Object
//	GetType (Method):  Public, Declared by System.Object
//	Finalize (Method):  Internal, Declared by System.Object
//	MemberwiseClone (Method):  Internal, Declared by System.Object
//	.ctor (Constructor):  Public, Declared by SimpleClass

Implizite Vererbung von der Object -Klasse macht diese Methoden der SimpleClass-Klasse verfügbar:Implicit inheritance from the Object class makes these methods available to the SimpleClass class:

  • Die öffentliche ToString-Methode, die ein SimpleClass-Objekt in seine Zeichenfolgendarstellung konvertiert, gibt den vollqualifizierten Typnamen zurück.The public ToString method, which converts a SimpleClass object to its string representation, returns the fully qualified type name. In diesem Fall gibt die ToString-Methode die Zeichenfolge „SimpleClass“ zurück.In this case, the ToString method returns the string "SimpleClass".

  • Drei Methoden, die zwei Objekte auf Gleichheit testen: die öffentliche Equals(Object)-Instanzmethode, die öffentliche statische Equals(Object, Object)-Methode und die öffentliche statische ReferenceEquals(Object, Object)-Methode.Three methods that test for equality of two objects: the public instance Equals(Object) method, the public static Equals(Object, Object) method, and the public static ReferenceEquals(Object, Object) method. Standardmäßig testen diese Methoden auf Verweisgleichheit; d.h., um gleich zu sein, müssen zwei Objektvariablen auf das gleiche Objekt verweisen.By default, these methods test for reference equality; that is, to be equal, two object variables must refer to the same object.

  • Die öffentliche GetHashCode-Methode, die einen Wert, berechnet, der die Verwendung einer Instanz des Typs in Hashauflistungen ermöglicht.The public GetHashCode method, which computes a value that allows an instance of the type to be used in hashed collections.

  • Die öffentliche GetType -Methode, die ein Type -Objekt zurückgibt, das den SimpleClass -Typ darstellt.The public GetType method, which returns a Type object that represents the SimpleClass type.

  • Die geschützte Finalize -Methode, die nicht verwaltete Ressourcen freigeben soll, bevor der Speicher eines Objekts durch den Garbage Collector freigegeben wird.The protected Finalize method, which is designed to release unmanaged resources before an object's memory is reclaimed by the garbage collector.

  • Die geschützte MemberwiseClone -Methode, die einen flachen Klon des aktuellen Objekts erstellt.The protected MemberwiseClone method, which creates a shallow clone of the current object.

Aufgrund der impliziten Vererbung können wir alle geerbten Member aus einem SimpleClass-Objekt einfach aufrufen, als wären sie tatsächlich in der SimpleClass-Klasse definierte Member.Because of implicit inheritance, we can call any inherited member from a SimpleClass object just as if it was actually a member defined in the SimpleClass class. Im folgenden Beispiel wird die SimpleClass.ToString-Methode aufgerufen, die SimpleClass von Object erbt.For instance, the following example calls the SimpleClass.ToString method, which SimpleClass inherits from Object.

using System;

public class SimpleClass
{}

public class Example
{
    public static void Main()
    {
        SimpleClass sc = new SimpleClass();
        Console.WriteLine(sc.ToString());
    }
}
// The example displays the following output:
//        SimpleClass

Die folgende Tabelle enthält die Kategorien von Typen, die Sie in C# erstellen können, und die Typen, von denen sie implizit erben.The following table lists the categories of types that you can create in C# and the types from which they implicitly inherit. Jeder Basistyp macht implizit abgeleiteten Typen über Vererbung einen anderen Satz von Membern verfügbar.Each base type makes a different set of members available through inheritance to implicitly derived types.

TypkategorieType category Erbt implizit vonImplicitly inherits from
Klasseclass Object
structstruct ValueType, ObjectValueType, Object
enumenum Enum, ValueType, ObjectEnum, ValueType, Object
delegatedelegate MulticastDelegate, Delegate, ObjectMulticastDelegate, Delegate, Object

Vererbung und eine „ist ein“-BeziehungInheritance and an "is a" relationship

Mit Vererbung wird normalerweise eine „ist ein“-Beziehung zwischen einer Basisklasse und einer oder mehreren abgeleiteten Klassen ausgedrückt, wobei die abgeleiteten Klassen spezialisierte Versionen der Basisklasse sind; die abgeleitete Klasse ist ein Typ der Basisklasse.Ordinarily, inheritance is used to express an "is a" relationship between a base class and one or more derived classes, where the derived classes are specialized versions of the base class; the derived class is a type of the base class. Die Publication-Klasse stellt z.B. eine Publikation beliebiger Art dar, und die Book- und Magazine-Klasse stellen bestimmte Typen von Publikationen dar.For example, the Publication class represents a publication of any kind, and the Book and Magazine classes represent specific types of publications.

Hinweis

Eine Klasse oder Struktur kann eine oder mehrere Schnittstellen implementieren.A class or struct can implement one more interfaces. Die Schnittstellenimplementierung wird zwar oft als Problemumgehung für einzelne Vererbung oder Möglichkeit der Verwendung von Vererbung mit Strukturen dargestellt, doch sie soll eine andere Beziehung (eine „tun können“-Beziehung) zwischen einer Schnittstelle und ihrem implementierenden Typ ausdrücken als Vererbung.While interface implementation is often presented as a workaround for single inheritance or as a way of using inheritance with structs, it is intended to express a different relationship (a "can do" relationship) between an interface and its implementing type than inheritance. Eine Schnittstelle definiert eine Teilmenge der Funktionalität (z.B. die Möglichkeit zum Testen auf Gleichheit, zum Vergleichen oder Sortieren von Objekten oder zum Unterstützen kulturspezifischer Analyse und Formatierung), die die Schnittstelle den implementierenden Typen zur Verfügung stellt.An interface defines a subset of functionality (such as the ability to test for equality, to compare or sort objects, or to support culture-sensitive parsing and formatting) that the interface makes available to its implementing types.

Beachten Sie, dass „ist ein“ auch die Beziehung zwischen einem Typ und einer bestimmten Instanziierung des betreffenden Typs ausdrückt.Note that "is a" also expresses the relationship between a type and a specific instantiation of that type. Im folgenden Beispiel ist Automobile eine Klasse mit drei eindeutigen schreibgeschützten Eigenschaften: Make, der Autohersteller; Model, den Autotyp, und Year, das Herstellungsjahr.In the following example, Automobile is a class that has three unique read-only properties: Make, the manufacturer of the automobile; Model, the kind of automobile; and Year, its year of manufacture. Unsere Automobile-Klasse verfügt auch über einen Konstruktor, dessen Argumente den Eigenschaftswerten zugewiesen werden, und er überschreibt die Object.ToString-Methode, um eine Zeichenfolge zu erzeugen, die eindeutig die Automobile-Instanz anstelle der Automobile-Klasse identifiziert.Our Automobile class also has a constructor whose arguments are assigned to the property values, and it overrides the Object.ToString method to produce a string that uniquely identifies the Automobile instance rather than the Automobile class.

using System;

public class Automobile
{
    public Automobile(string make, string model, int year)
    {
        if (make == null)
           throw new ArgumentNullException("The make cannot be null.");
        else if (String.IsNullOrWhiteSpace(make))
           throw new ArgumentException("make cannot be an empty string or have space characters only.");
        Make = make;

        if (model == null)
           throw new ArgumentNullException("The model cannot be null.");
        else if (String.IsNullOrWhiteSpace(make))
           throw new ArgumentException("model cannot be an empty string or have space characters only.");
        Model = model;

        if (year < 1857 || year > DateTime.Now.Year + 2)
           throw new ArgumentException("The year is out of range.");
        Year = year;
    }

    public string Make { get; }
    
    public string Model { get; }

    public int Year { get; }

    public override string ToString() => $"{Year} {Make} {Model}";
}

In diesem Fall sollten wir uns nicht auf die Vererbung verlassen, um bestimmte Automarken und Modelle darzustellen.In this case, we should not rely on inheritance to represent specific car makes and models. Wir müssen z.B. keinen Packard-Typ definieren, um Autos darzustellen, die von der Packard Motor Car Company hergestellt werden.For example, we do not need to define a Packard type to represent automobiles manufactured by the Packard Motor Car Company. Stattdessen können wir sie durch Erstellen eines Automobile-Objekts darstellen, wobei die entsprechenden Werten an dessen Klassenkonstruktor übergeben werden, wie es im folgenden Beispiel geschieht.Instead, we can represent them by creating an Automobile object with the appropriate values passed to its class constructor, as the following example does.

using System;

public class Example
{
    public static void Main()
    {
        var packard = new Automobile("Packard", "Custom Eight", 1948);
        Console.WriteLine(packard);
    }
}
// The example displays the following output:
//        1948 Packard Custom Eight

Eine auf Vererbung basierende „ist ein“-Beziehung wird am besten auf eine Basisklasse und abgeleitete Klassen angewendet, die der Basisklasse weitere Member hinzufügen oder zusätzliche Funktionalität erfordern, die in der Basisklasse nicht vorhanden ist.An is-a relationship based on inheritance is best applied to a base class and to derived classes that add additional members to the base class or that require additional functionality not present in the base class.

Entwerfen der Basisklasse und abgeleiteter KlassenDesigning the base class and derived classes

Wir betrachten das Entwerfen einer Basisklasse und ihrer abgeleiteten Klassen.Let's look at the process of designing a base class and its derived classes. In diesem Abschnitt definieren wir eine Basisklasse Publication, die eine Publikation jeder Art darstellt, z.B. ein Buch, eine Zeitschrift, eine Zeitung, ein Journal, einen Artikel usw. Wir definieren auch eine Book-Klasse, die von Publication abgeleitet ist.In this section, we'll define a base class, Publication, which represents a publication of any kind, such as a book, a magazine, a newspaper, a journal, an article, etc. We'll also define a Book class that derives from Publication. Wir könnten das Beispiel einfach erweitern, um andere abgeleitete Klassen wie Magazine, Journal, Newspaper und Article zu definieren.We could easily extend the example to define other derived classes, such as Magazine, Journal, Newspaper, and Article.

Die Basisklasse „Publication“The base Publication class

Beim Entwurf unserer Publication-Klasse müssen wir einige Entwurfsentscheidungen treffen:In designing our Publication class, we need to make several design decisions:

  • Welche Member sollen in unsere Basis-Publication-Klasse einbezogen werden? Sollen die Publication-Member Methodenimplementierungen bereitstellen, oder ist Publication eine abstrakte Basisklasse, die als Vorlage für ihre abgeleiteten Klassen dient?What members to include in our base Publication class, and whether the Publication members provide method implementations, or whether Publication is an abstract base class that serves as a template for its derived classes.

    In diesem Fall stellt die Publication-Klasse Methodenimplementierungen bereit.In this case, the Publication class will provide method implementations. Der Abschnitt Entwerfen abstrakter Basisklassen und ihrer abgeleiteten Klassen enthält ein Beispiel, in dem eine abstrakte Basisklasse verwendet wird, um die Methoden zu definieren, die abgeleitete Klassen überschreiben müssen.The Designing abstract base classes amd their derived classes section contains an example that uses an abstract base class to define the methods that derived classes must override. Abgeleitete Klassen können beliebige Implementierungen bereitstellen, die für den abgeleiteten Typ geeignet sind.Derived classes are free to provide any implementation that is suitable for the derived type.

    Die Möglichkeit zur Wiederverwendung von Code (d.h., mehrere abgeleitete Klassen nutzen gemeinsam die Deklaration und Implementierung von Basisklassenmethoden und müssen sie nicht überschreiben) ist ein Vorteil der nicht abstrakten Basisklassen.The ability to reuse code (that is, multiple derived classes share the declaration and implementation of base class methods and do not need to override them) is an advantage of non-abstract base classes. Daher sollten wir Member zu Publication hinzufügen, wenn ihr Code vermutlich von einigen oder den meisten spezialisierten Publication-Typen gemeinsam genutzt wird.Therefore, we should add members to Publication if their code is likely to be shared by some or most specialized Publication types. Wenn wir dies nicht effizient durchführen, müssen wir letztendlich weitgehend identische Implementierungen von Membern in abgeleiteten Klassen bereitstellen, statt einer einzelnen Implementierung in der Basisklasse.If we fail to do this efficiently, we'll end up having to provide largely identical member implementations in derived classes rather a single implementation in the base class. Die Notwendigkeit, duplizierten Code an mehreren Standorten zu verwalten, ist eine potenzielle Fehlerquelle.The need to maintain duplicated code in multiple locations is a potential source of bugs.

    Um sowohl die Wiederverwendung von Code zu maximieren als auch eine logische und intuitive Vererbungshierarchie zu erstellen, möchten wir sicher sein, dass wir in die Publication-Klasse nur die Daten und Funktionen einbeziehen, die alle bzw. die meisten Publikationen gemeinsam haben.Both to maximize code reuse and to create a logical and intuitive inheritance hierarchy, we want to be sure that we include in the Publication class only the data and functionality that is common to all or to most publications. Abgeleitete Klassen implementieren dann Member, die für die jeweiligen Publikationsarten, die sie darstellen, eindeutig sind.Derived classes then implement members that are unique to the particular kinds of publication that they represent.

  • Wie weit sollen wir unsere Klassenhierarchie ausdehnen?How far to extend our class hierarchy. Möchten wir statt nur einer Basisklasse und einer oder mehreren abgeleiteten Klassen eine Hierarchie von drei oder mehr Klassen entwickeln?Do we want to develop a hierarchy of three or more classes, rather than simply a base class and one or more derived classes? Beispielsweise könnte Publication eine Basisklasse von Periodical sein, was wiederum eine Basisklasse von Magazine, Journal und Newspaper ist.For example, Publication could be a base class of Periodical, which in turn is a base class of Magazine, Journal and Newspaper.

    Für unser Beispiel verwenden wir die einfache Hierarchie einer Publication-Klasse und einer einzelnen abgeleiteten Klasse, Book.For our example, we'll use the simple hierarchy of a Publication class and a single derived classes, Book. Wir könnten das Beispiel mühelos erweitern, um eine Reihe von zusätzlichen Klassen zu erstellen, die von Publication abgeleitet sind, wie z.B. Magazine und Article.We could easily extend the example to create a number of additional classes that derive from Publication, such as Magazine and Article.

  • Ist es sinnvoll, die Basisklasse zu instanziieren?Whether it makes sense to instantiate the base class. Wenn dies nicht der Fall ist, sollten wir das Schlüsselwort abstract auf die Klasse anwenden.If it does not, we should apply the abstract keyword to the class. Wenn versucht wird, eine mit dem abstract-Schlüsselwort markierte Klasse durch einen direkten Aufruf ihres Klassenkonstruktors zu instanziieren, generiert der C#-Compiler den Fehler CS0144: „Es konnte keine Instanz der abstrakten Klasse oder Schnittstelle erstellt werden.“If an attempt is made to instantiate a class marked with the abstract keyword by a direct call to its class constructor, the C# compiler generates error CS0144, "Cannot create an instance of the abstract class or interface." Wenn versucht wird, die Klasse mithilfe der Reflektion zu instanziieren, löst die Reflektionsmethode eine MemberAccessException aus.If an attempt is made to instantiate the class by using reflection, the reflection method throws a MemberAccessException. Andernfalls kann unsere Publication-Klasse durch Aufruf ihres Klassenkonstruktors instanziiert werden.Otherwise, our Publication class can be instantiated by calling its class constructor.

    Standardmäßig kann eine Basisklasse durch Aufruf ihres Klassenkonstruktors instanziiert werden.By default, a base class can be instantiated by calling its class constructor. Beachten Sie, dass wir nicht explizit einen Klassenkonstruktor definieren müssen.Note that we do not have to explicitly define a class constructor. Wenn im Quellcode der Basisklasse keiner vorhanden ist, stellt der C#-Compiler automatisch einen (parameterlosen) Standardkonstruktor bereit.If one is not present in the base class' source code, the C# compiler automatically provides a default (parameterless) constructor.

    In unserem Beispiel markieren wir die Publication-Klasse als abstract, sodass sie nicht instanziiert werden kann.For our example, we'll mark the Publication class as abstract so that it cannot be instantiated.

  • Müssen abgeleitete Klassen die Implementierung der Basisklasse eines bestimmten Members erben, oder können sie optional die Implementierung der Basisklasse überschreiben?Whether derived classes must inherit the base class implementation of a particular members, or whether they have the option to override the base class implementation. Um abgeleiteten Klassen zu erlauben, eine Basisklassenmethode zu überschreiben, müssen wir das virtual-Schlüsselwort verwenden.We have to use the virtual keyword to allow derived classes to override a base class method. Standardmäßig können in der Basisklasse definierte Methoden nicht überschrieben werden.By default, methods defined in the base class are not overridable.

  • Stellt eine abgeleitete Klasse die endgültige Klasse in der Vererbungshierarchie dar und kann nicht selbst als Basisklasse für weitere abgeleitete Klassen verwendet werden?Whether a derived class represents the final class in the inheritance hierarchy and cannot itself be used as a base class for additional derived classes. Standardmäßig kann jede Klasse als Basisklasse dienen.By default, any class can serve as a base class. Wir können das sealed-Schlüsselwort anwenden, um anzugeben, dass eine Klasse nicht als Basisklasse für zusätzliche Klassen dienen kann.We can apply the sealed keyword to indicate that a class cannot serve as a base class for any additional classes. Beim Versuch der Ableitung von einer versiegelten Klasse wird der Compilerfehler CS0509 generiert: „Vom versiegelten Typ kann nicht abgeleitet werden“.Attempting to derive from a sealed class generated compiler error CS0509, "cannot derive from sealed type ".

    In unserem Beispiel markieren wir unsere abgeleitete Klasse als sealed.For our example, we'll mark our derived class as sealed.

Das folgende Beispiel zeigt sowohl den Quellcode für die Publication-Klasse als auch eine PublicationType-Enumeration, die von der Eigenschaft Publication.PublicationType zurückgegeben wird.The following example shows the source code for the Publication class, as well as a PublicationType enumeration that is returned by the Publication.PublicationType property. Zusätzlich zu den Membern, die sie von Object erbt, definiert die Publication-Klasse die folgenden eindeutigen Member und Memberüberschreibungen:In addition to the members that it inherits from Object, the Publication class defines the following unique members and member overrides:

using System;

public enum PublicationType { Misc, Book, Magazine, Article };

public abstract class Publication
{
   private bool published = false;
   private DateTime datePublished;
   private int totalPages; 

   public Publication(string title, string publisher, PublicationType type)
   {
      if (publisher == null)
         throw new ArgumentNullException("The publisher cannot be null.");
      else if (String.IsNullOrWhiteSpace(publisher))
         throw new ArgumentException("The publisher cannot consist only of whitespace.");
      Publisher = publisher;
  
      if (title == null)
         throw new ArgumentNullException("The title cannot be null.");
      else if (String.IsNullOrWhiteSpace(title))
         throw new ArgumentException("The title cannot consist only of whitespace.");
      Title = title;

      Type = type;
   }

   public string Publisher { get; }

   public string Title { get; }

   public PublicationType Type { get; }

   public string CopyrightName { get; private set; }
   
   public int CopyrightDate { get; private set; }

   public int Pages
   {
     get { return totalPages; }
     set 
     {
         if (value <= 0)
            throw new ArgumentOutOfRangeException("The number of pages cannot be zero or negative.");
         totalPages = value;   
     }
   }

   public string GetPublicationDate()
   {
      if (!published)
         return "NYP";
      else
         return datePublished.ToString("d");   
   }
   
   public void Publish(DateTime datePublished)
   {
      published = true;
      this.datePublished = datePublished;
   }

   public void Copyright(string copyrightName, int copyrightDate)
   {
      if (copyrightName == null)
         throw new ArgumentNullException("The name of the copyright holder cannot be null.");
      else if (String.IsNullOrWhiteSpace(copyrightName))
         throw new ArgumentException("The name of the copyright holder cannot consist only of whitespace.");
      CopyrightName = copyrightName;
      
      int currentYear = DateTime.Now.Year;
      if (copyrightDate < currentYear - 10 || copyrightDate > currentYear + 2)
         throw new ArgumentOutOfRangeException($"The copyright year must be between {currentYear -10} and {currentYear + 1}");
            CopyrightDate = copyrightDate;      
   }

   public override string ToString() => Title;
}
  • Ein KonstruktorA constructor

    Da die Publication-Klasse abstract ist, kann sie nicht direkt von Code wie dem folgenden aus instanziiert werden:Because the Publication class is abstract, it cannot be instantiated directly from code like the following:

    var publication = new Publication("Tiddlywinks for Experts", "Fun and Games",
                                      PublicationType.Book);
    

    Ihr Instanzkonstruktor kann jedoch direkt von abgeleiteten Klassenkonstruktoren aufgerufen werden, wie der Quellcode für die Book-Klasse veranschaulicht.However, its instance constructor can be called directly from derived class constructors, as the source code for the Book class shows.

  • Zwei publikationsbezogene EigenschaftenTwo publication-related properties

    Title ist eine schreibgeschützte String-Eigenschaft, deren Wert durch Aufrufen des Publication-Konstruktors bereitgestellt wird.Title is a read-only String property whose value is supplied by calling the Publication constructor.

    Pages ist eine schreibgeschützte Eigenschaft Int32, die angibt, wie viele Seiten die Publikation insgesamt hat.Pages is a read-write Int32 property that indicates how many total pages the publication has. Der Wert wird in einem privaten Feld namens totalPages gespeichert.The value is stored in a private field named totalPages. Er muss eine positive Zahl sein; andernfalls wird eine ArgumentOutOfRangeException ausgelöst.It must be a positive number or an ArgumentOutOfRangeException is thrown.

  • Herausgeberbezogene ElementePublisher-related members

    Zwei schreibgeschützte Eigenschaften, Publisher und Type.Two read-only properties, Publisher and Type. Die Werte werden ursprünglich durch den Aufruf des Publication-Klassenkonstruktors abgerufen.The values are originally supplied by the call to the Publication class constructor.

  • Veröffentlichungsbezogene ElementePublishing-related members

    Zwei Methoden, Publish und GetPublicationDate, legen das Veröffentlichungsdatum fest und geben es zurück.Two methods, Publish and GetPublicationDate, set and return the publication date. Die Publish-Methode setzt ein privates published-Flag auf true, wenn sie aufgerufen wird, und weist das ihr übergebene Datum als Argument dem privaten Feld datePublished zu.The Publish method sets a private published flag to true when it is called and assigns the date passed to it as an argument to the private datePublished field. Die GetPublicationDate-Methode gibt die Zeichenfolge „NYP“ zurück, wenn das published-Flag false ist, und den Wert des Felds datePublished, wenn es true ist.The GetPublicationDate method returns the string "NYP" if the published flag is false, and the value of the datePublished field if it is true.

  • Copyrightbezogene ElementeCopyright-related members

    Die Methode Copyright übernimmt den Namen des Urheberrechtsinhabers und das Jahr des Copyrights als Argumente und weist sie den Eigenschaften CopyrightName und CopyrightDate zu.The Copyright method takes the name of the copyright holder and the year of the copyright as arguments and assigns them to the CopyrightName and CopyrightDate properties.

  • Eine Überschreibung der ToString-MethodeAn override of the ToString method

    Wenn ein Typ die Object.ToString -Methode nicht überschreibt, gibt sie den vollqualifizierten Namen des Typs zurück, was zur Unterscheidung einer Instanz von einer anderen von geringem Nutzen ist.If a type does not override the Object.ToString method, it returns the fully qualified name of the type, which is of little use in differentiating one instance from another. Die Publication-Klasse überschreibt Object.ToString, um den Wert der Eigenschaft Title zurückzugeben.The Publication class overrides Object.ToString to return the value of the Title property.

Die folgende Abbildung veranschaulicht die Beziehung zwischen unserer Basis-Publication -Klasse und der implizit geerbten Object -Klasse.The following figure illustrates the relationship between our base Publication class and its implicitly inherited Object class.

Die Klassen „Object“ und „Publication“

Die Book-KlasseThe Book class

Die Book-Klasse stellt ein Buch als einen speziellen Typ der Publikation dar.The Book class represents a book as a specialized type of publication. Das folgende Beispiel zeigt den Quellcode für die Book-Klasse.The following example shows the source code for the Book class.

using System;

public sealed class Book : Publication
{
   public Book(string title, string author, string publisher) : 
          this(title, String.Empty, author, publisher)
   { }

   public Book(string title, string isbn, string author, string publisher) : base(title, publisher, PublicationType.Book)
   {
      // isbn argument must be a 10- or 13-character numeric string without "-" characters.
      // We could also determine whether the ISBN is valid by comparing its checksum digit 
      // with a computed checksum.
      //
      if (! String.IsNullOrEmpty(isbn)) {
        // Determine if ISBN length is correct.
        if (! (isbn.Length == 10 | isbn.Length == 13))
            throw new ArgumentException("The ISBN must be a 10- or 13-character numeric string.");
        ulong nISBN = 0;
        if (! UInt64.TryParse(isbn, out nISBN))
            throw new ArgumentException("The ISBN can consist of numeric characters only.");
      } 
      ISBN = isbn;

      Author = author;
   }
     
   public string ISBN { get; }

   public string Author { get; }
   
   public Decimal Price { get; private set; }

   // A three-digit ISO currency symbol.
   public string Currency { get; private set; }
   

   // Returns the old price, and sets a new price.
   public Decimal SetPrice(Decimal price, string currency)
   {
       if (price < 0)
          throw new ArgumentOutOfRangeException("The price cannot be negative.");
       Decimal oldValue = Price;
       Price = price;
       
       if (currency.Length != 3)
          throw new ArgumentException("The ISO currency symbol is a 3-character string.");
       Currency = currency;

       return oldValue;      
   }

   public override bool Equals(object obj)
   {
      Book book = obj as Book;
      if (book == null)
         return false;
      else
         return ISBN == book.ISBN;   
   }

   public override int GetHashCode() => ISBN.GetHashCode();

   public override string ToString() => $"{(String.IsNullOrEmpty(Author) ? "" : Author + ", ")}{Title}"; 
}

Zusätzlich zu den Membern, die sie von Publication erbt, definiert die Book-Klasse die folgenden eindeutigen Member und Memberüberschreibungen:In addition to the members that it inherits from Publication, the Book class defines the following unique members and member overrides:

  • Zwei KonstruktorenTwo constructors

    Die beiden Book-Konstruktoren nutzen gemeinsam drei allgemeine Parameter.The two Book constructors share three common parameters. Zwei, title und publisher, entsprechen den Parametern des Publication-Konstruktors.Two, title and publisher, correspond to parameters of the Publication constructor. Der dritte ist author, der in einem privaten Feld authorName gespeichert ist.The third is author, which is stored to a private authorName field. Ein Konstruktor enthält einen ISBN-Parameter, der in der Auto-Eigenschaft ISBN gespeichert ist.One constructor includes an isbn parameter, which is stored in the ISBN auto-property.

    Der erste Konstruktor verwendet das this-Schlüsselwort, um den anderen Konstruktor aufzurufen.The first constructor uses the this keyword to call the other constructor. Dies ist ein häufiges Muster beim Definieren von Konstruktoren.This is a common pattern in defining constructors. Konstruktoren mit weniger Parametern stellen beim Aufrufen des Konstruktors mit der größten Anzahl von Parametern Standardwerte zur Verfügung.Constructors with fewer parameters provide default values when calling the constructor with the greatest number of parameters.

    Der zweite Konstruktor verwendet das base-Schlüsselwort, um Titel und Herausgebername an den Basisklassenkonstruktor zu übergeben.The second constructor uses the base keyword to pass the title and publisher name to the base class constructor. Wenn Ihr Quellcode keinen expliziten Aufruf eines Basisklassenkonstruktors enthält, stellt der C#-Compiler automatisch einen Aufruf des standardmäßigen oder parameterlosen Konstruktors der Basisklasse bereit.If you don't make an explicit call to a base class constructor in your source code, the C# compiler automatically supplies a call to the base class' default or parameterless constructor.

  • Eine schreibgeschützte Eigenschaft ISBN, die die ISBN des Book-Objekts zurückgibt, eine eindeutige 10- oder 13-stellige Nummer.A read-only ISBN property, which returns the Book object's International Standard Book Number, a unique 10- or 13-digit number. Die ISBN wird einem der Book-Konstruktoren als Argument übergeben.The ISBN is supplied as an argument to one of the Book constructors. Die ISBN wird in einem privaten Unterstützungsfeld gespeichert, das automatisch vom Compiler generiert wird.The ISBN is stored in a private backing field, which is auto-generated by the compiler.

  • Eine schreibgeschützte Eigenschaft Author.A read-only Author property. Der Autorenname wird als Argument beiden Book-Konstruktoren übergeben und im privaten Feld authorName gespeichert.The author name is supplied as an argument to both Book constructors and is stored in the private authorName field.

  • Zwei schreibgeschützte preisbezogene Eigenschaften, Price und Currency.Two read-only price-related properties, Price and Currency. Ihre Werte werden in einem Aufruf der SetPrice-Methode als Argumente bereitgestellt.Their values are provided as arguments in a SetPrice method call. Der Preis wird in einem privaten Feld namens bookPrice gespeichert.The price is stored in a private field, bookPrice. Die Eigenschaft Currency ist das dreistellige ISO-Währungssymbol (z.B. USD für den US-Dollar) und befindet sich im privaten Feld ISOCurrencySymbol.The Currency property is the three-digit ISO currency symbol (for example, USD for the U.S. dollar) and is stored in the private ISOCurrencySymbol field. ISO-Währungssymbole können aus der Eigenschaft ISOCurrencySymbol abgerufen werden.ISO currency symbols can be retrieved from the ISOCurrencySymbol property.

  • Eine SetPrice-Methode, die die Werte der Felder bookPrice und ISOCurrencySymbol festlegt.A SetPrice method, which sets the values of the bookPrice and ISOCurrencySymbol fields. Dies sind die Rückgabewerte der Eigenschaften Price und Currency.These are the values returned by the Price and Currency properties.

  • Überschreibt die ToString-Methode (geerbt von Publication) und die Methoden Object.Equals(Object) und GetHashCode (geerbt von Object).Overrides to the ToString method (inherited from Publication) and the Object.Equals(Object) and GetHashCode methods (inherited from Object).

    Sofern sie nicht überschrieben wird, führt die Methode Object.Equals(Object) Tests hinsichtlich der Verweisgleichheit durch.Unless it is overridden, the Object.Equals(Object) method tests for reference equality. D.h., zwei Objektvariablen werden als gleich betrachtet, wenn sie auf das gleiche Objekt verweisen.That is, two object variables are considered to be equal if they refer to the same object. Andererseits sollten im Fall der Book-Klasse zwei Book-Objekte gleich sein, wenn sie die gleiche ISBN haben.In the case of the Book class, on the other hand, two Book objects should be equal if they have the same ISBN.

    Wenn Sie die Object.Equals(Object)-Methode überschreiben, müssen Sie auch die GetHashCode-Methode überschreiben. Diese gibt einen Wert zurück, den die Laufzeit zum Speichern von Elementen in Hashauflistungen für einen effizienten Abruf verwendet.When you override the Object.Equals(Object) method, you must also override the GetHashCode method, which returns a value that the runtime uses to store items in hashed collections for efficient retrieval. Der Hashcode sollte einen Wert zurückgeben, der mit dem Test auf Gleichheit konsistent ist.The hash code should return a value that's consistent with the test for equality. Da wir Object.Equals(Object) überschrieben haben, sodass true zurückgegeben wird, wenn die ISBN-Eigenschaften von zwei Book-Objekten gleich sind, geben wir den Hashcode zurück, der durch Aufrufen der GetHashCode-Methode der von der Eigenschaft ISBN zurückgegebenen Zeichenfolge berechnet wurde.Since we've overridden Object.Equals(Object) to return true if the ISBN properties of two Book objects are equal, we return the hash code computed by calling the GetHashCode method of the string returned by the ISBN property.

Die folgende Abbildung veranschaulicht die Beziehung zwischen der Book-Klasse und Publication, ihrer Basisklasse.The following figure illustrates the relationship between the Book class and Publication, its base class.

Die Klassen „Publication“ und „Book“

Wir können jetzt ein Book-Objekt instanziieren, sowohl dessen eindeutige als auch geerbte Member aufrufen und es als Argument an eine Methode übergeben, die einen Parameter des Typs Publication oder Book erwartet, wie im folgenden Beispiel gezeigt.We can now instantiate a Book object, invoke both its unique and inherited members, and pass it as an argument to a method that expects a parameter of type Publication or of type Book, as the following example shows.

using System;
using static System.Console;

public class Example
{
   public static void Main()
   {
      var book = new Book("The Tempest",  "0971655819", "Shakespeare, William",
                          "Public Domain Press");
      ShowPublicationInfo(book);
      book.Publish(new DateTime(2016, 8, 18));
      ShowPublicationInfo(book);

      var book2 = new Book("The Tempest", "Classic Works Press", "Shakespeare, William");
      Write($"{book.Title} and {book2.Title} are the same publication: " +
            $"{((Publication) book).Equals(book2)}");
   }

   public static void ShowPublicationInfo(Publication pub)
   {
       string pubDate = pub.GetPublicationDate();
       WriteLine($"{pub.Title}, " +
                 $"{(pubDate == "NYP" ? "Not Yet Published" : "published on " + pubDate):d} by {pub.Publisher}"); 
   }
}
// The example displays the following output:
//        The Tempest, Not Yet Published by Public Domain Press
//        The Tempest, published on 8/18/2016 by Public Domain Press
//        The Tempest and The Tempest are the same publication: False

Entwerfen abstrakter Basisklassen und der von ihnen abgeleiteten KlassenDesigning abstract base classes and their derived classes

Im vorherigen Beispiel definierten wir eine Basisklasse, die eine Implementierung für eine Reihe von Methoden bereitstellte, um abgeleiteten Klassen die gemeinsame Nutzung von Code zu erlauben.In the previous example, we defined a base class that provided an implementation for a number of methods to allow derived classes to share code. In vielen Fällen wird jedoch nicht erwartet, dass die Basisklasse eine Implementierung bereitstellt.In many cases, however, the base class is not expected to provide an implementation. Stattdessen ist die Basisklasse eine abstrakte Klasse; sie dient als Vorlage, die die Member definiert, die jede abgeleitete Klasse implementieren muss.Instead, the base class is an abstract class; it serves as a template that defines the members that each derived class must implement. Im Fall einer abstrakten Basisklasse ist die Implementierung jedes abgeleiteten Typs in der Regel für diesen Typ eindeutig.Typically in the case of an abstract base class, the implementation of each derived type is unique to that type.

Jede geschlossene zweidimensionale geometrische Form besitzt beispielsweise zwei Eigenschaften: den Flächeninhalt, die innere Ausdehnung der Form; und den Umfang, d.h. die Länge der Kanten der Form.For example, each closed two-dimensional geometric shape includes two properties: area, the inner extent of the shape; and perimeter, or the distance along the edges of the shape. Wie diese Eigenschaften berechnet werden, hängt jedoch vollständig von der jeweiligen Form ab.The way in which these properties are calculated, however, depends completely on the specific shape. Die Formel zum Berechnen des Umfangs eines Kreises unterscheidet sich z.B. grundlegend von der zum Berechnen des Umfangs eines Dreiecks.The formula for calculating the perimeter (or circumference) of a circle, for example, is very different from that of a triangle.

Das folgende Beispiel definiert eine abstrakte Basisklasse mit dem Namen Shape, die zwei Eigenschaften definiert: Area und Perimeter.The following example defines an abstract base class named Shape that defines two properties: Area and Perimeter. Beachten Sie, dass zusätzlich zum Markieren der Klasse mit dem abstract-Schlüsselwort auch jeder Instanzmember mit dem abstract-Schlüsselwort markiert wird.Note that, in addition to marking the class with the abstract keyword, each instance member is also marked with the abstract keyword. In diesem Fall überschreibt Shape auch die Object.ToString -Methode, um den Namen des Typs anstelle dessen vollqualifizierten Namens zurückzugeben.In this case, Shape also overrides the Object.ToString method to return the name of the type, rather than its fully qualified name. Außerdem definiert sie zwei statische Member, GetArea und GetPerimeter, die Aufrufern ermöglichen, mühelos Fläche und Umfang einer Instanz einer beliebigen abgeleiteten Klasse abzurufen.And it defines two static members, GetArea and GetPerimeter, that allow callers to easily retrieve the area and perimeter of an instance of any derived class. Wenn wir eine Instanz einer abgeleiteten Klasse an eine der beiden Methoden übergeben, ruft die Laufzeit die Methodenüberschreibung der abgeleiteten Klasse auf.When we pass an instance of a derived class to either of these methods, the runtime calls the method override of the derived class.

using System;

public abstract class Shape
{
   public abstract double Area { get; }
   
   public abstract double Perimeter { get; }
 
   public override string ToString() => GetType().Name;

   public static double GetArea(Shape shape) => shape.Area;

   public static double GetPerimeter(Shape shape) => shape.Perimeter;
}

Dann können wir einige Klassen von Shape ableiten, die bestimmte Formen darstellen.We can then derive some classes from Shape that represent specific shapes. Das folgende Beispiel definiert drei Klassen, Triangle, Rectangle und Circle.The following example defines three classes, Triangle, Rectangle, and Circle. Jede verwendet eine Formel, die für die Berechnung von Fläche und Umfang der betreffenden Form eindeutig ist.Each uses a formula unique for that particular shape to compute the area and perimeter. Einige der abgeleiteten Klassen definieren auch Eigenschaften, z.B. Rectangle.Diagonal und Circle.Diameter, die für die Form, die sie darstellen, eindeutig sind.Some of the derived classes also define properties, such as Rectangle.Diagonal and Circle.Diameter, that are unique to the shape that they represent.

using System;

public class Square : Shape
{
   public Square(double length)
   {
      Side = length;
   }

   public double Side { get; }  

   public override double Area => Math.Pow(Side, 2); 

   public override double Perimeter => Side * 4;

   public double Diagonal => Math.Round(Math.Sqrt(2) * Side, 2); 
}

public class Rectangle : Shape
{
   public Rectangle(double length, double width)
   {
      Length = length;
      Width = width;
   }

   public double Length { get; }

   public double Width { get; }

   public override double Area => Length * Width;

   public override double Perimeter => 2 * Length + 2 * Width;  

   public bool IsSquare() => Length == Width;

   public double Diagonal => Math.Round(Math.Sqrt(Math.Pow(Length, 2) + Math.Pow(Width, 2)), 2); 
}

public class Circle : Shape
{
   public Circle(double radius)
   {
      Radius = radius;
   } 

   public override double Area => Math.Round(Math.PI * Math.Pow(Radius, 2), 2); 

   public override double Perimeter => Math.Round(Math.PI * 2 * Radius, 2); 

   // Define a circumference, since it's the more familiar term.
   public double Circumference => Perimeter; 

   public double Radius { get; }

   public double Diameter => Radius * 2; 
}

Im folgenden Beispiel werden von Shape abgeleitete Objekte verwendet.The following example uses objects derived from Shape. Es instanziiert ein Array von Objekten, die von Shape abgeleitet sind, und ruft die statischen Methoden der Shape-Klasse auf, deren Umschließungen Shape-Eigenschaftswerte zurückgeben.It instantiates an array of objects derived from Shape and calls the static methods of the Shape class, which wraps return Shape property values. Beachten Sie, dass die Laufzeit Werte aus den überschriebenen Eigenschaften der abgeleiteten Typen abruft.Note that the runtime retrieves values from the overridden properties of the derived types. Das Beispiel wandelt auch jedes Shape-Objekt im Array in seinen abgeleiteten Typ um, und wenn die Umwandlung erfolgreich ist, ruft es Eigenschaften dieser bestimmten Unterklasse von Shape auf.The example also casts each Shape object in the array to its derived type and, if the cast succeeds, retrieves properties of that particular subclass of Shape.

using System;

public class Example
{
   public static void Main()
   {
      Shape[] shapes = { new Rectangle(10, 12), new Square(5),
                        new Circle(3) };
      foreach (var shape in shapes) {
         Console.WriteLine($"{shape}: area, {Shape.GetArea(shape)}; " +
                           $"perimeter, {Shape.GetPerimeter(shape)}");
         var rect = shape as Rectangle;
         if (rect != null) {
            Console.WriteLine($"   Is Square: {rect.IsSquare()}, Diagonal: {rect.Diagonal}");
            continue;
         }
         var sq = shape as Square;
         if (sq != null) {
            Console.WriteLine($"   Diagonal: {sq.Diagonal}");
            continue;
         }
      }   
   }
}
// The example displays the following output:
//         Rectangle: area, 120; perimeter, 44
//            Is Square: False, Diagonal: 15.62
//         Square: area, 25; perimeter, 20
//            Diagonal: 7.07
//         Circle: area, 28.27; perimeter, 18.85

Siehe auchSee also

Klassen und Objekte Classes and objects
Vererbung (C#-Programmierhandbuch)Inheritance (C# Programming Guide)