Dziedziczenie w języku C# i .NETInheritance in C# and .NET

Ten samouczek przedstawia sposób dziedziczenia w języku C#.This tutorial introduces you to inheritance in C#. Dziedziczenie to funkcja języków programowania zorientowanego obiektowo, która umożliwia zdefiniowanie klasy bazowej, która zapewnia konkretne funkcje (dane i zachowanie) i definiuje klasy pochodne, które dziedziczą lub przesłaniają te funkcje.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.

Wymagania wstępnePrerequisites

W tym samouczku założono, że zainstalowano zestaw .NET Core SDK.This tutorial assumes that you've installed the .NET Core SDK. Odwiedź stronę pobierania programu .NET Core , aby ją pobrać.Visit the .NET Core Downloads page to download it. Potrzebny jest również Edytor kodu.You also need a code editor. W tym samouczku użyto Visual Studio Code, chociaż można użyć dowolnego dowolnego edytora kodu.This tutorial uses Visual Studio Code, although you can use any code editor of your choice.

Uruchamianie przykładówRunning the examples

Aby utworzyć i uruchomić przykłady w tym samouczku, użyj narzędzia dotnet z wiersza polecenia.To create and run the examples in this tutorial, you use the dotnet utility from the command line. Wykonaj następujące czynności na każdym z przykładów:Follow these steps for each example:

  1. Utwórz katalog do przechowywania przykładu.Create a directory to store the example.

  2. Wprowadź nową konsolę programu dotnet w wierszu polecenia, aby utworzyć nowy projekt .NET Core.Enter the dotnet new console command at a command prompt to create a new .NET Core project.

  3. Skopiuj i wklej kod z przykładu do edytora kodu.Copy and paste the code from the example into your code editor.

  4. Wprowadź dotnet Restore polecenie w wierszu polecenia, aby załadować lub przywrócić zależności projektu.Enter the dotnet restore command from the command line to load or restore the project's dependencies.

    Nie trzeba uruchamiać programu dotnet restore , ponieważ jest on uruchamiany niejawnie przez wszystkie polecenia, które wymagają wykonania przywracania, takie jak,,,, dotnet new dotnet build dotnet run dotnet test dotnet publish i dotnet pack .You don't have to run dotnet restore because it's run implicitly by all commands that require a restore to occur, such as dotnet new, dotnet build, dotnet run, dotnet test, dotnet publish, and dotnet pack. Aby wyłączyć Przywracanie niejawne, użyj --no-restore opcji.To disable implicit restore, use the --no-restore option.

    dotnet restorePolecenie jest nadal przydatne w niektórych scenariuszach, w których jawne jest przywracanie, takie jak kompilacje ciągłej integracji w Azure DevOps Services lub w systemach kompilacji, które muszą jawnie kontrolować po wystąpieniu przywracania.The dotnet restore command is still useful in certain scenarios where explicitly restoring makes sense, such as continuous integration builds in Azure DevOps Services or in build systems that need to explicitly control when the restore occurs.

    Informacje o sposobach zarządzania źródłami danych NuGet znajdują się w dotnet restore dokumentacji.For information about how to manage NuGet feeds, see the dotnet restore documentation.

  5. Wprowadź polecenie Uruchom jako dotnet , aby skompilować i uruchomić przykład.Enter the dotnet run command to compile and execute the example.

Tło: co to jest dziedziczenie?Background: What is inheritance?

Dziedziczenie jest jednym z podstawowych atrybutów programowania zorientowanego obiektowo.Inheritance is one of the fundamental attributes of object-oriented programming. Umożliwia zdefiniowanie klasy podrzędnej, która ponownie używa (dziedziczy), rozszerza lub modyfikuje zachowanie klasy nadrzędnej.It allows you to define a child class that reuses (inherits), extends, or modifies the behavior of a parent class. Klasa, której członkowie są dziedziczone jest nazywana klasą bazową.The class whose members are inherited is called the base class. Klasa, która dziedziczy elementy członkowskie klasy bazowej, nosi nazwę klasy pochodnej.The class that inherits the members of the base class is called the derived class.

Języki C# i .NET obsługują tylko pojedyncze dziedziczenie .C# and .NET support single inheritance only. Oznacza to, że Klasa może dziedziczyć tylko z pojedynczej klasy.That is, a class can only inherit from a single class. Dziedziczenie jest jednak przechodnie, co pozwala na zdefiniowanie hierarchii dziedziczenia dla zestawu typów.However, inheritance is transitive, which allows you to define an inheritance hierarchy for a set of types. Innymi słowy, typ D może dziedziczyć po typie C , który dziedziczy z typu B , który dziedziczy z typu klasy bazowej A .In other words, type D can inherit from type C, which inherits from type B, which inherits from the base class type A. Ponieważ dziedziczenie jest przechodnie, elementy członkowskie typu A są dostępne do typu D .Because inheritance is transitive, the members of type A are available to type D.

Nie wszystkie składowe klasy bazowej są dziedziczone przez klasy pochodne.Not all members of a base class are inherited by derived classes. Następujące elementy członkowskie nie są dziedziczone:The following members are not inherited:

  • Konstruktory statyczne, które inicjują statyczne dane klasy.Static constructors, which initialize the static data of a class.

  • Konstruktory wystąpień, które są wywoływane w celu utworzenia nowego wystąpienia klasy.Instance constructors, which you call to create a new instance of the class. Każda klasa musi definiować własne konstruktory.Each class must define its own constructors.

  • Finalizatory, które są wywoływane przez moduł wyrzucania elementów bezużytecznych środowiska uruchomieniowego, aby zniszczyć wystąpienia klasy.Finalizers, which are called by the runtime's garbage collector to destroy instances of a class.

Wszystkie inne elementy członkowskie klasy bazowej są dziedziczone przez klasy pochodne, niezależnie od tego, czy są one widoczne, czy nie są zależne od ich dostępności.While all other members of a base class are inherited by derived classes, whether they are visible or not depends on their accessibility. Dostępność elementu członkowskiego ma wpływ na widoczność klas pochodnych w następujący sposób:A member's accessibility affects its visibility for derived classes as follows:

  • Prywatne składowe są widoczne tylko w klasach pochodnych, które są zagnieżdżone w klasie podstawowej.Private members are visible only in derived classes that are nested in their base class. W przeciwnym razie nie są one widoczne w klasach pochodnych.Otherwise, they are not visible in derived classes. W poniższym przykładzie A.B jest klasą zagnieżdżoną, która pochodzi od A , i C pochodzi od A .In the following example, A.B is a nested class that derives from A, and C derives from A. Pole prywatne A.value jest widoczne w A.B.The private A.value field is visible in A.B. Jeśli jednak usuniesz Komentarze z C.GetValue metody i spróbujemy skompilować przykład, generuje błąd kompilatora CS0122: "A. Value" jest niedostępny z powodu swojego poziomu ochrony ".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
    
  • Chronione elementy członkowskie są widoczne tylko w klasach pochodnych.Protected members are visible only in derived classes.

  • Wewnętrzne elementy członkowskie są widoczne tylko w klasach pochodnych, które znajdują się w tym samym zestawie, co Klasa bazowa.Internal members are visible only in derived classes that are located in the same assembly as the base class. Nie są one widoczne w klasach pochodnych znajdujących się w innym zestawie z klasy podstawowej.They are not visible in derived classes located in a different assembly from the base class.

  • Publiczne składowe są widoczne w klasach pochodnych i są częścią interfejsu publicznego klasy pochodnej.Public members are visible in derived classes and are part of the derived class' public interface. Publiczne dziedziczone elementy członkowskie mogą być wywoływane tak, jakby są zdefiniowane w klasie pochodnej.Public inherited members can be called just as if they are defined in the derived class. W poniższym przykładzie Klasa A definiuje metodę o nazwie Method1 , a Klasa B dziedziczy z klasy A .In the following example, class A defines a method named Method1, and class B inherits from class A. Przykład następnie wywołuje Method1 , jakby była metodą wystąpienia 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();
        }
    }
    

Klasy pochodne mogą również przesłaniać dziedziczone elementy członkowskie, dostarczając alternatywną implementację.Derived classes can also override inherited members by providing an alternate implementation. Aby możliwe było przesłonięcie elementu członkowskiego, element członkowski w klasie bazowej musi być oznaczony za pomocą słowa kluczowego Virtual .In order to be able to override a member, the member in the base class must be marked with the virtual keyword. Domyślnie elementy członkowskie klasy bazowej nie są oznaczone jako virtual i nie można ich zastąpić.By default, base class members are not marked as virtual and cannot be overridden. Próba zastąpienia niewirtualnego elementu członkowskiego, jak pokazano w poniższym przykładzie, generuje błąd kompilatora CS0506: " <member> nie można przesłonić dziedziczonej składowej, <member> ponieważ nie jest oznaczona jako wirtualna, abstrakcyjna lub przesłonięcie.Attempting to override a non-virtual member, as the following example does, generates compiler error CS0506: "<member> cannot override inherited member <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.
    }
}

W niektórych przypadkach Klasa pochodna musi przesłonić implementację klasy bazowej.In some cases, a derived class must override the base class implementation. Elementy członkowskie klasy bazowej oznaczone za pomocą słowa kluczowego abstract wymagają, aby klasy pochodne zostały przez nie zastąpione.Base class members marked with the abstract keyword require that derived classes override them. Próba skompilowania poniższego przykładu generuje błąd kompilatora CS0534, " < Klasa nie > implementuje dziedziczonej abstrakcyjnej < składowej składowej > ", ponieważ Klasa nie B zapewnia implementacji dla A.Method1 .Attempting to compile the following example generates compiler error CS0534, "<class> does not implement inherited abstract member <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.
    }
}

Dziedziczenie ma zastosowanie tylko do klas i interfejsów.Inheritance applies only to classes and interfaces. Inne kategorie typów (struktury, Delegaty i wyliczenia) nie obsługują dziedziczenia.Other type categories (structs, delegates, and enums) do not support inheritance. Ze względu na te reguły Próba skompilowania kodu, takiego jak Poniższy przykład, powoduje wystąpienie błędu kompilatora CS0527: "typ" ValueType "na liście interfejsów nie jest interfejsem".Because of these rules, attempting to compile code like the following example produces compiler error CS0527: "Type 'ValueType' in interface list is not an interface." Komunikat o błędzie wskazuje, że chociaż można zdefiniować interfejsy, które implementuje struktura, dziedziczenie nie jest obsługiwane.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.
{
}

Dziedziczenie niejawneImplicit inheritance

Oprócz typów, które mogą dziedziczyć za pośrednictwem pojedynczego dziedziczenia, wszystkie typy w systemie typu .NET niejawnie dziedziczą z Object lub typu pochodnego.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. Typowe funkcje programu Object są dostępne dla dowolnego typu.The common functionality of Object is available to any type.

Aby zobaczyć, co oznacza niejawne dziedziczenie, zdefiniujmy nową klasę, SimpleClass która jest po prostu pustą definicją klasy:To see what implicit inheritance means, let's define a new class, SimpleClass, that is simply an empty class definition:

public class SimpleClass
{ }

Następnie można użyć odbicia, które umożliwia sprawdzenie metadanych typu w celu uzyskania informacji na temat tego typu, aby uzyskać listę elementów członkowskich należących do SimpleClass typu.You can then use reflection (which lets you inspect a type's metadata to get information about that type) to get a list of the members that belong to the SimpleClass type. Chociaż nie zdefiniowano żadnych elementów członkowskich w SimpleClass klasie, dane wyjściowe z przykładu wskazują, że faktycznie ma dziewięć członków.Although you haven't defined any members in your SimpleClass class, output from the example indicates that it actually has nine members. Jeden z tych elementów członkowskich jest konstruktorem bez parametrów (lub domyślnym), który jest automatycznie dostarczany dla SimpleClass typu przez kompilator języka C#.One of these members is a parameterless (or default) constructor that is automatically supplied for the SimpleClass type by the C# compiler. Pozostałe osiem są członkami Object , typu, z którego wszystkie klasy i interfejsy w systemie typu .NET ostatecznie dziedziczą.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

Niejawne dziedziczenie z Object klasy sprawia, że te metody są dostępne dla SimpleClass klasy:Implicit inheritance from the Object class makes these methods available to the SimpleClass class:

  • Metoda publiczna ToString , która konwertuje SimpleClass obiekt na jego reprezentację w postaci ciągu, zwraca w pełni kwalifikowaną nazwę typu.The public ToString method, which converts a SimpleClass object to its string representation, returns the fully qualified type name. W tym przypadku ToString Metoda zwraca ciąg "SimpleClass".In this case, the ToString method returns the string "SimpleClass".

  • Trzy metody, które testują pod kątem równości dwóch obiektów: publiczna Equals(Object) metoda wystąpienia, publiczna metoda statyczna Equals(Object, Object) i publiczna metoda statyczna ReferenceEquals(Object, Object) .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. Domyślnie te metody testują równość odniesienia; oznacza to, że jest równe, dwie zmienne obiektów muszą odwoływać się do tego samego obiektu.By default, these methods test for reference equality; that is, to be equal, two object variables must refer to the same object.

  • Metoda publiczna GetHashCode , która oblicza wartość umożliwiającą wystąpienie typu, który ma być używany w kolekcjach skrótów.The public GetHashCode method, which computes a value that allows an instance of the type to be used in hashed collections.

  • Metoda publiczna GetType , która zwraca Type obiekt, który reprezentuje SimpleClass Typ.The public GetType method, which returns a Type object that represents the SimpleClass type.

  • Metoda chroniona Finalize , która została zaprojektowana w celu zwolnienia zasobów niezarządzanych przed odjęciem pamięci obiektu przez moduł wyrzucania elementów bezużytecznych.The protected Finalize method, which is designed to release unmanaged resources before an object's memory is reclaimed by the garbage collector.

  • Metoda chroniona MemberwiseClone , która tworzy płytki klonowania bieżącego obiektu.The protected MemberwiseClone method, which creates a shallow clone of the current object.

Ze względu na niejawne dziedziczenie można wywołać dowolny Dziedziczony element członkowski z SimpleClass obiektu, tak jakby był to element członkowski zdefiniowany w SimpleClass klasie.Because of implicit inheritance, you can call any inherited member from a SimpleClass object just as if it was actually a member defined in the SimpleClass class. Na przykład poniższy przykład wywołuje SimpleClass.ToString metodę, która SimpleClass dziedziczy z Object .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

W poniższej tabeli wymieniono kategorie typów, które można utworzyć w języku C#, oraz typy, z których dziedziczą niejawnie.The following table lists the categories of types that you can create in C# and the types from which they implicitly inherit. Każdy typ podstawowy udostępnia inny zestaw elementów członkowskich za pomocą dziedziczenia do niejawnie pochodnych typów.Each base type makes a different set of members available through inheritance to implicitly derived types.

Kategoria typuType category Niejawnie dziedziczone zImplicitly inherits from
classclass Object
strukturastruct ValueType, ObjectValueType, Object
enumenum Enum, ValueType, ObjectEnum, ValueType, Object
delegatedelegate MulticastDelegate, Delegate, ObjectMulticastDelegate, Delegate, Object

Dziedziczenie i relacja "is"Inheritance and an "is a" relationship

Zwykle dziedziczenie jest stosowane do wyrażenia "is" relacji między klasą bazową i co najmniej jedną klasą pochodną, gdzie klasy pochodne są wyspecjalizowanymi wersjami klasy bazowej; Klasa pochodna jest typem klasy bazowej.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. Na przykład Publication Klasa reprezentuje publikację dowolnego rodzaju, a Book Magazine klasy i reprezentują określone typy publikacji.For example, the Publication class represents a publication of any kind, and the Book and Magazine classes represent specific types of publications.

Uwaga

Klasa lub struktura może zaimplementować jeden lub więcej interfejsów.A class or struct can implement one or more interfaces. Podczas gdy implementacja interfejsu jest często prezentowana jako obejście dla pojedynczego dziedziczenia lub jako sposób używania dziedziczenia z strukturami, należy przedstawić inną relację (relację "może") między interfejsem i jego typem implementującym niż dziedziczenie.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. Interfejs definiuje podzestaw funkcji (takich jak możliwość testowania równości, porównywania lub sortowania obiektów lub do obsługi analizy zależnej od kultury i formatowania), które Interfejs udostępnia dla jego typów implementujących.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.

Należy zauważyć, że "is" także wskazuje relację między typem i określonym wystąpieniem tego typu.Note that "is a" also expresses the relationship between a type and a specific instantiation of that type. W poniższym przykładzie Automobile jest klasą, która ma trzy unikatowe właściwości tylko do odczytu: Make , producent urządzenia przenośnego Model , rodzaj samochodów, a Year rok produkcji.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. AutomobileKlasa ma także konstruktora, którego argumenty są przypisane do wartości właściwości, i zastępuje Object.ToString metodę, aby utworzyć ciąg, który jednoznacznie identyfikuje Automobile wystąpienie, a nie Automobile klasę.Your 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(model))
           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}";
}

W takim przypadku nie należy polegać na dziedziczeniu do reprezentowania określonych samochodów i modeli.In this case, you shouldn't rely on inheritance to represent specific car makes and models. Nie trzeba na przykład definiować Packard typu do reprezentowania samochodów, które są wytwarzane przez firmę Packard Motor samochodowa.For example, you don't need to define a Packard type to represent automobiles manufactured by the Packard Motor Car Company. Zamiast tego można je przedstawić, tworząc Automobile obiekt z odpowiednimi wartościami przekazaną do jego konstruktora klasy, jak pokazano w poniższym przykładzie.Instead, you 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

Jest to relacja oparta na dziedziczeniu najlepiej zastosowana do klasy bazowej i do klas pochodnych, które dodają do klasy podstawowej dodatkowe elementy członkowskie lub które wymagają dodatkowych funkcji nieobecnych w klasie podstawowej.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.

Projektowanie klasy bazowej i klas pochodnychDesigning the base class and derived classes

Przyjrzyjmy się procesowi projektowania klasy bazowej i jej klas pochodnych.Let's look at the process of designing a base class and its derived classes. W tej sekcji zdefiniujesz klasę bazową, Publication która reprezentuje publikację dowolnego rodzaju, na przykład książkę, czasopismo, gazetę, arkusz, artykuł itd. Zdefiniujesz również Book klasę, która pochodzi od Publication .In this section, you'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. You'll also define a Book class that derives from Publication. Można łatwo zwiększyć przykład, aby zdefiniować inne klasy pochodne, takie jak Magazine ,, Journal Newspaper , i Article .You could easily extend the example to define other derived classes, such as Magazine, Journal, Newspaper, and Article.

Klasa publikacji podstawowejThe base Publication class

Podczas projektowania Publication klasy należy podjąć pewne decyzje dotyczące projektowania:In designing your Publication class, you need to make several design decisions:

  • Elementy członkowskie, które mają zostać uwzględnione w Publication klasie bazowej, oraz informacje o tym, czy Publication składowe udostępniają implementacje metod, czy też Publication jest abstrakcyjną klasą bazową, która służy jako szablon klas pochodnych.What members to include in your 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.

    W takim przypadku Klasa zapewni Publication implementacje metod.In this case, the Publication class will provide method implementations. Projektowanie abstrakcyjnych klas podstawowych i ich klas pochodnych zawiera przykład, który używa abstrakcyjnej klasy bazowej do definiowania metod, które muszą zostać przesłonięte przez klasy pochodne.The Designing abstract base classes and their derived classes section contains an example that uses an abstract base class to define the methods that derived classes must override. Klasy pochodne są bezpłatne, aby zapewnić implementację, która jest odpowiednia dla typu pochodnego.Derived classes are free to provide any implementation that is suitable for the derived type.

    Możliwość ponownego użycia kodu (oznacza to, że wiele klas pochodnych korzysta z deklaracji i implementacji metod klasy bazowej i nie trzeba ich przesłonić) jest zaletą nieabstrakcyjnych klas podstawowych.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. W związku z tym należy dodać członków do, Publication jeśli ich kod może być współużytkowany przez niektóre lub większość wyspecjalizowanych Publication typów.Therefore, you should add members to Publication if their code is likely to be shared by some or most specialized Publication types. Jeśli nie podasz wydajnych implementacji klas podstawowych, będziesz mieć możliwość zapewnienia większości identycznych implementacji elementów członkowskich w klasach pochodnych, a nie pojedynczej implementacji w klasie bazowej.If you fail to provide base class implementations efficiently, you'll end up having to provide largely identical member implementations in derived classes rather a single implementation in the base class. Konieczność utrzymania duplikatu kodu w wielu lokalizacjach jest potencjalnym źródłem błędów.The need to maintain duplicated code in multiple locations is a potential source of bugs.

    Aby zmaksymalizować użycie kodu i utworzyć logiczną i intuicyjną hierarchię dziedziczenia, należy się upewnić, że należy uwzględnić w Publication klasie tylko dane i funkcje wspólne dla wszystkich lub większości publikacji.Both to maximize code reuse and to create a logical and intuitive inheritance hierarchy, you want to be sure that you include in the Publication class only the data and functionality that is common to all or to most publications. Klasy pochodne następnie implementują elementy członkowskie, które są unikatowe dla określonych rodzajów publikacji, które reprezentują.Derived classes then implement members that are unique to the particular kinds of publication that they represent.

  • Jak daleko, aby zwiększyć hierarchię klas.How far to extend your class hierarchy. Czy chcesz opracować hierarchię trzech lub więcej klas, a nie po prostu klasy bazowej i co najmniej jednej klasy pochodnej?Do you want to develop a hierarchy of three or more classes, rather than simply a base class and one or more derived classes? Na przykład Publication może być klasą bazową Periodical , która z kolei jest klasą bazową Magazine , Journal i Newspaper .For example, Publication could be a base class of Periodical, which in turn is a base class of Magazine, Journal and Newspaper.

    Przykładowo będziesz używać małej hierarchii Publication klasy i pojedynczej klasy pochodnej Book .For your example, you'll use the small hierarchy of a Publication class and a single derived class, Book. Można łatwo zwiększyć przykład, aby utworzyć szereg dodatkowych klas, które pochodzą z Publication , takich jak Magazine i Article .You could easily extend the example to create a number of additional classes that derive from Publication, such as Magazine and Article.

  • Określa, czy warto utworzyć wystąpienie klasy bazowej.Whether it makes sense to instantiate the base class. Jeśli tak nie jest, należy zastosować abstrakcyjne słowo kluczowe do klasy.If it does not, you should apply the abstract keyword to the class. W przeciwnym razie Publication można utworzyć wystąpienie klasy, wywołując jej Konstruktor klas.Otherwise, your Publication class can be instantiated by calling its class constructor. Jeśli zostanie podjęta próba utworzenia wystąpienia klasy oznaczonej za pomocą abstract słowa kluczowego przez bezpośrednie wywołanie konstruktora klasy, kompilator języka C# generuje błąd CS0144, "nie można utworzyć wystąpienia klasy abstrakcyjnej lub interfejsu".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." Jeśli zostanie podjęta próba utworzenia wystąpienia klasy przy użyciu odbicia, Metoda odbicia zgłasza MemberAccessException .If an attempt is made to instantiate the class by using reflection, the reflection method throws a MemberAccessException.

    Domyślnie można utworzyć wystąpienie klasy bazowej, wywołując jej Konstruktor klas.By default, a base class can be instantiated by calling its class constructor. Nie musisz jawnie definiować konstruktora klasy.You do not have to explicitly define a class constructor. Jeśli nie występuje w kodzie źródłowym klasy podstawowej, kompilator języka C# automatycznie udostępnia domyślny Konstruktor (bez parametrów).If one is not present in the base class' source code, the C# compiler automatically provides a default (parameterless) constructor.

    Przykładowo oznaczmy Publication klasę jako abstrakcyjną , aby nie można było utworzyć wystąpienia.For your example, you'll mark the Publication class as abstract so that it cannot be instantiated. abstractKlasa bez żadnych abstract metod wskazuje, że ta klasa reprezentuje abstrakcyjną koncepcję, która jest współużytkowana przez kilka klas konkretnych (takich jak Book , Journal ).An abstract class without any abstract methods indicates that this class represents an abstract concept that is shared among several concrete classes (like a Book, Journal).

  • Bez względu na to, czy klasy pochodne muszą dziedziczyć implementację klasy bazowej określonych elementów członkowskich, czy mają opcję przesłaniania implementacji klasy bazowej, czy też muszą dostarczyć implementację.Whether derived classes must inherit the base class implementation of particular members, whether they have the option to override the base class implementation, or whether they must provide an implementation. Aby wymusić implementację klas pochodnych, należy użyć słowa kluczowego abstract .You use the abstract keyword to force derived classes to provide an implementation. Możesz użyć słowa kluczowego Virtual , aby umożliwić klasom pochodnym przesłonięcie metody klasy bazowej.You use the virtual keyword to allow derived classes to override a base class method. Domyślnie metody zdefiniowane w klasie bazowej nie są możliwe do zastąpienia.By default, methods defined in the base class are not overridable.

    PublicationKlasa nie ma żadnych abstract metod, ale sama klasa to abstract .The Publication class does not have any abstract methods, but the class itself is abstract.

  • Czy Klasa pochodna reprezentuje ostateczną klasę w hierarchii dziedziczenia i nie może być używana jako klasa bazowa dla dodatkowych klas pochodnych.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. Domyślnie każda klasa może być klasą bazową.By default, any class can serve as a base class. Można zastosować zapieczętowane słowo kluczowe, aby wskazać, że Klasa nie może być klasą bazową dla jakichkolwiek dodatkowych klas.You can apply the sealed keyword to indicate that a class cannot serve as a base class for any additional classes. Podjęto próbę wygenerowania z wygenerowanej klasy błędu kompilatora CS0509 "nie może pochodzić od typu zapieczętowanego <typeName> ".Attempting to derive from a sealed class generated compiler error CS0509, "cannot derive from sealed type <typeName>".

    Na przykład należy oznaczyć klasę pochodną jako sealed .For your example, you'll mark your derived class as sealed.

Poniższy przykład pokazuje kod źródłowy dla Publication klasy, a także PublicationType Wyliczenie, które jest zwracane przez Publication.PublicationType Właściwość.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. Oprócz elementów członkowskich, z których dziedziczy Object , Publication Klasa definiuje następujące unikatowe elementy członkowskie i przesłonięcia elementu członkowskiego: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 (String.IsNullOrWhiteSpace(publisher))
         throw new ArgumentException("The publisher is required.");
      Publisher = publisher;

      if (String.IsNullOrWhiteSpace(title))
         throw new ArgumentException("The title is required.");
      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 (String.IsNullOrWhiteSpace(copyrightName))
         throw new ArgumentException("The name of the copyright holder is required.");
      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;
}
  • KonstruktorA constructor

    Ponieważ Publication Klasa jest abstract , nie można utworzyć wystąpienia bezpośrednio w kodzie, podobnie jak w poniższym przykładzie:Because the Publication class is abstract, it cannot be instantiated directly from code like the following example:

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

    Jednak jego Konstruktor wystąpień można wywołać bezpośrednio z konstruktorów klas pochodnych, ponieważ kod źródłowy Book klasy pokazuje.However, its instance constructor can be called directly from derived class constructors, as the source code for the Book class shows.

  • Dwie właściwości powiązane z publikacjąTwo publication-related properties

    Titlejest właściwością tylko do odczytu String , której wartość jest dostarczana przez wywołanie Publication konstruktora.Title is a read-only String property whose value is supplied by calling the Publication constructor.

    Pagesjest właściwością do odczytu i zapisu Int32 , która wskazuje, ile razem stron publikacji ma.Pages is a read-write Int32 property that indicates how many total pages the publication has. Wartość jest przechowywana w prywatnym polu o nazwie totalPages .The value is stored in a private field named totalPages. Musi być liczbą dodatnią lub ArgumentOutOfRangeException jest generowany.It must be a positive number or an ArgumentOutOfRangeException is thrown.

  • Elementy członkowskie powiązane z wydawcąPublisher-related members

    Dwie właściwości tylko do odczytu Publisher i Type .Two read-only properties, Publisher and Type. Wartości są początkowo dostarczane przez wywołanie Publication konstruktora klasy.The values are originally supplied by the call to the Publication class constructor.

  • Elementy członkowskie powiązane z publikowaniemPublishing-related members

    Dwie metody, Publish i GetPublicationDate , ustawiają i zwracają datę publikacji.Two methods, Publish and GetPublicationDate, set and return the publication date. PublishMetoda ustawia published flagę prywatną na true kiedy jest wywoływana i przypisuje datę przekazaną do niej jako argument do pola prywatnego datePublished .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. GetPublicationDateMetoda zwraca ciąg "NYP" published , jeśli flaga jest false i wartość datePublished pola, jeśli jest true .The GetPublicationDate method returns the string "NYP" if the published flag is false, and the value of the datePublished field if it is true.

  • Elementy członkowskie powiązane z prawami autorskimiCopyright-related members

    CopyrightMetoda przyjmuje nazwę posiadacza praw autorskich i rok praw autorskich jako argumenty i przypisuje je do CopyrightName CopyrightDate właściwości i.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.

  • Przesłonięcie ToString metodyAn override of the ToString method

    Jeśli typ nie przesłania Object.ToString metody, zwraca w pełni kwalifikowaną nazwę typu, który jest niewielkim użyciem w rozróżnieniu jednego wystąpienia od drugiego.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. PublicationKlasa przesłania Object.ToString wartość Title właściwości.The Publication class overrides Object.ToString to return the value of the Title property.

Na poniższej ilustracji przedstawiono relacje między Publication klasą bazową i jej niejawnie dziedziczoną Object klasą.The following figure illustrates the relationship between your base Publication class and its implicitly inherited Object class.

Klasy obiektów i publikacji

BookKlasaThe Book class

BookKlasa reprezentuje książkę jako wyspecjalizowany typ publikacji.The Book class represents a book as a specialized type of publication. Poniższy przykład pokazuje kod źródłowy dla Book klasy.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}";
}

Oprócz elementów członkowskich, z których dziedziczy Publication , Book Klasa definiuje następujące unikatowe elementy członkowskie i przesłonięcia elementu członkowskiego:In addition to the members that it inherits from Publication, the Book class defines the following unique members and member overrides:

  • Dwa konstruktoryTwo constructors

    Dwa Book konstruktory korzystają z trzech wspólnych parametrów.The two Book constructors share three common parameters. Dwa, tytuły i Wydawcaodpowiadają parametrom Publication konstruktora.Two, title and publisher, correspond to parameters of the Publication constructor. Trzecia jest autorem, który jest przechowywany w publicznej niezmiennej Author właściwości.The third is author, which is stored to a public immutable Author property. Jeden Konstruktor zawiera parametr ISBN , który jest przechowywany we ISBN Właściwości autoproperty.One constructor includes an isbn parameter, which is stored in the ISBN auto-property.

    Pierwszy Konstruktor używa słowa kluczowego this do wywołania innego konstruktora.The first constructor uses the this keyword to call the other constructor. Tworzenie łańcucha konstruktorów jest typowym wzorcem definiującym konstruktory.Constructor chaining is a common pattern in defining constructors. Konstruktory z mniejszą liczbą parametrów zapewniają wartości domyślne podczas wywoływania konstruktora przy użyciu największej liczby parametrów.Constructors with fewer parameters provide default values when calling the constructor with the greatest number of parameters.

    Drugi Konstruktor używa słowa kluczowego Base do przekazania tytułu i nazwy wydawcy do konstruktora klasy bazowej.The second constructor uses the base keyword to pass the title and publisher name to the base class constructor. Jeśli nie utworzysz jawnie wywołania konstruktora klasy bazowej w kodzie źródłowym, kompilator języka C# automatycznie poda wywołanie do konstruktora klasy podstawowej "Default lub bez parametrów.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.

  • Właściwość tylko do odczytu ISBN , która zwraca Book międzynarodowy numer standardowej księgi obiektu, unikatowy numer 10 lub 13 cyfr.A read-only ISBN property, which returns the Book object's International Standard Book Number, a unique 10- or 13-digit number. ISBN jest dostarczany jako argument do jednego z Book konstruktorów.The ISBN is supplied as an argument to one of the Book constructors. ISBN jest przechowywany w prywatnym polu zapasowym, które jest automatycznie generowane przez kompilator.The ISBN is stored in a private backing field, which is auto-generated by the compiler.

  • Właściwość tylko do odczytu Author .A read-only Author property. Nazwa autora jest dostarczana jako argument do obu Book konstruktorów i jest przechowywana we właściwości.The author name is supplied as an argument to both Book constructors and is stored in the property.

  • Dwie właściwości dotyczące cen tylko do odczytu Price i Currency .Two read-only price-related properties, Price and Currency. Ich wartości są podawane jako argumenty w SetPrice wywołaniu metody.Their values are provided as arguments in a SetPrice method call. CurrencyWłaściwość jest symbolem waluty ISO o trzech cyfrach (na przykład USD dla dolara USA).The Currency property is the three-digit ISO currency symbol (for example, USD for the U.S. dollar). Symbole waluty ISO można pobrać z ISOCurrencySymbol właściwości.ISO currency symbols can be retrieved from the ISOCurrencySymbol property. Obie te właściwości są zewnętrznie tylko do odczytu, ale obie można ustawić przez kod w Book klasie.Both of these properties are externally read-only, but both can be set by code in the Book class.

  • SetPriceMetoda, która ustawia wartości Price Currency właściwości i.A SetPrice method, which sets the values of the Price and Currency properties. Te wartości są zwracane przez te same właściwości.Those values are returned by those same properties.

  • Przesłania do ToString metody (dziedziczone z Publication ) i Object.Equals(Object) GetHashCode metody i (dziedziczone z Object ).Overrides to the ToString method (inherited from Publication) and the Object.Equals(Object) and GetHashCode methods (inherited from Object).

    O ile nie zostanie on zastąpiony, Object.Equals(Object) Metoda sprawdza równość odwołań.Unless it is overridden, the Object.Equals(Object) method tests for reference equality. Oznacza to, że dwie zmienne obiektu są uważane za równe, jeśli odwołują się do tego samego obiektu.That is, two object variables are considered to be equal if they refer to the same object. W Book klasie, z drugiej strony, dwa Book obiekty powinny być równe, jeśli mają ten sam ISBN.In the Book class, on the other hand, two Book objects should be equal if they have the same ISBN.

    Podczas przesłonięcia Object.Equals(Object) metody należy również zastąpić GetHashCode metodę, która zwraca wartość używaną przez środowisko uruchomieniowe do przechowywania elementów w kolekcjach skrótów w celu wydajnego pobierania.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. Kod skrótu powinien zwracać wartość zgodną z testem dla równości.The hash code should return a value that's consistent with the test for equality. Ponieważ zastąpienie Object.Equals(Object) true Właściwości ISBN dwóch Book obiektów jest równe, zwracany jest kod skrótu obliczony przez wywołanie GetHashCode metody ciągu zwracanego przez ISBN Właściwość.Since you've overridden Object.Equals(Object) to return true if the ISBN properties of two Book objects are equal, you return the hash code computed by calling the GetHashCode method of the string returned by the ISBN property.

Na poniższej ilustracji przedstawiono relacje między Book klasą a Publication klasą bazową.The following figure illustrates the relationship between the Book class and Publication, its base class.

Klasy publikacji i książek

Teraz można utworzyć wystąpienie Book obiektu, wywołać zarówno unikatowy, jak i dziedziczonych elementów członkowskich, i przekazać go jako argument do metody, która oczekuje parametru typu Publication lub typu Book , jak pokazano w poniższym przykładzie.You 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

Projektowanie abstrakcyjnych klas podstawowych i ich klas pochodnychDesigning abstract base classes and their derived classes

W poprzednim przykładzie zdefiniowano klasę bazową, która udostępnia implementację wielu metod, aby umożliwić pochodnym klasom współużytkowanie kodu.In the previous example, you defined a base class that provided an implementation for a number of methods to allow derived classes to share code. W wielu przypadkach jednak Klasa bazowa nie powinna dostarczyć implementacji.In many cases, however, the base class is not expected to provide an implementation. Zamiast tego Klasa bazowa jest klasą abstrakcyjną , która deklaruje metody abstrakcyjne; służy jako szablon definiujący składowe, które muszą zostać zaimplementowane przez każdą klasę pochodną.Instead, the base class is an abstract class that declares abstract methods; it serves as a template that defines the members that each derived class must implement. Zwykle w abstrakcyjnej klasie podstawowej implementacja każdego typu pochodnego jest unikatowa dla tego typu.Typically in an abstract base class, the implementation of each derived type is unique to that type. Klasa jest oznaczona za pomocą abstrakcyjnego słowa kluczowego, ponieważ nie ma sensu tworzenia wystąpienia Publication obiektu, chociaż Klasa zapewniała implementacje wspólnych funkcji dla publikacji.You marked the class with the abstract keyword because it made no sense to instantiate a Publication object, although the class did provide implementations of functionality common to publications.

Na przykład każdy zamknięty dwuwymiarowy kształt geometryczny zawiera dwie właściwości: obszar, wewnętrzny zakres kształtu; i obwód oraz odległość wzdłuż krawędzi kształtu.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. Jednak sposób, w jaki te właściwości są obliczane, zależy całkowicie od określonego kształtu.The way in which these properties are calculated, however, depends completely on the specific shape. Formuła służąca do obliczania obwodu (lub obwodu) koła, na przykład, różni się od tego trójkąta.The formula for calculating the perimeter (or circumference) of a circle, for example, is different from that of a triangle. ShapeKlasa jest abstract klasą z abstract metodami.The Shape class is an abstract class with abstract methods. Wskazuje, że klasy pochodne mają takie same funkcje, ale te klasy pochodne implementują te funkcje w różny sposób.That indicates derived classes share the same functionality, but those derived classes implement that functionality differently.

W poniższym przykładzie zdefiniowano abstrakcyjną klasę bazową o nazwie Shape , która definiuje dwie właściwości: Area i Perimeter .The following example defines an abstract base class named Shape that defines two properties: Area and Perimeter. Oprócz oznaczania klasy abstrakcyjnym słowem kluczowym każdy element członkowski wystąpienia jest również oznaczony za pomocą słowa kluczowego abstract .In addition to marking the class with the abstract keyword, each instance member is also marked with the abstract keyword. W takim przypadku Shape przesłania również Object.ToString metodę zwracającą nazwę typu, a nie jego w pełni kwalifikowaną nazwę.In this case, Shape also overrides the Object.ToString method to return the name of the type, rather than its fully qualified name. Definiuje dwa statyczne elementy członkowskie GetArea i GetPerimeter , które umożliwiają wywołującym łatwe pobieranie obszaru i obwodu wystąpienia dowolnej klasy pochodnej.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. Gdy przekazujesz wystąpienie klasy pochodnej do jednej z tych metod, środowisko uruchomieniowe wywołuje metodę przesłaniania klasy pochodnej.When you 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;
}

Następnie można utworzyć klasy z Shape tego, które reprezentują określone kształty.You can then derive some classes from Shape that represent specific shapes. W poniższym przykładzie zdefiniowano trzy klasy, Triangle , Rectangle , i Circle .The following example defines three classes, Triangle, Rectangle, and Circle. Każda z nich używa formuły unikatowej dla danego kształtu do obliczenia obszaru i obwodu.Each uses a formula unique for that particular shape to compute the area and perimeter. Niektóre klasy pochodne definiują również właściwości, takie jak Rectangle.Diagonal i Circle.Diameter , które są unikatowe dla kształtu, który reprezentuje.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;
}

W poniższym przykładzie zastosowano obiekty pochodne od Shape .The following example uses objects derived from Shape. Tworzy wystąpienie obiektu Array obiektów pochodzących od Shape i wywołuje metody statyczne Shape klasy, co spowoduje Zawijanie zwracanych Shape wartości właściwości.It instantiates an array of objects derived from Shape and calls the static methods of the Shape class, which wraps return Shape property values. Środowisko uruchomieniowe pobiera wartości z przesłoniętych właściwości typów pochodnych.The runtime retrieves values from the overridden properties of the derived types. Przykład rzutuje również każdy Shape obiekt w tablicy na jego typ pochodny i, jeśli rzutowanie powiedzie się, pobiera właściwości tej konkretnej podklasy Shape .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

Zobacz teżSee also