【C#プログラミング】継承、インターフェース

1. 継承

1、派生

        継承により、類似しているが異なる概念間のクラス階層の概念が確立されます。より一般的なクラスは基本クラスと呼ばれ、より具体的なクラスは派生クラスと呼ばれます。派生クラスは、基本クラスのすべてのプロパティを継承します。

        派生クラスを定義するには、クラス識別子の後にコロンを追加し、その後に基本クラス名を追加します。

public class PdaItem
{
    public string Name { get; set; }
    public DateTime LastUpdated { get; set; }
}

// Define the Contact class as inheriting the PdaItem class
public class Contact : PdaItem
{
    public string Address { get; set; }
    public string Phone { get; set; }
}

        基本クラスが明示的に指定されていない限り、デフォルトではすべてのクラスがオブジェクトから派生します。

2. 基底クラスと派生クラスの変換

        派生によって「に属する」関係が確立されるため、いつでも派生クラスの値を基本型の変数に割り当てることができます。これを暗黙的な型変換と呼びます。基本型から派生型への変換には、明示的なキャストが必要です。変換対象の型を元の参照名の前に括弧で囲む方法です。

public static void Main()
{
    // Derived types can be implicitly converted to base types
    Contact contact = new Contact();
    PdaItem item = contact;//隐式类型转换

    // Base types must be cast explicitly to derived types 显示类型转换/强制类型转换
    contact = (Contact)item; 
    // ...
}

3. カスタム変換

        型間の変換は、単一の継承チェーン内の型に限定されません。異なる型を相互に変換することもできます。重要なのは、2 つの型間に変換演算子を提供することです。C# では、明示的または暗黙的なキャスト演算子を使用できます。例えば:

class GPSCoordinates
{
    public static implicit operator UTMCoordinates(GPSCoordinates coordinates)
    { 
        // ...
        return null;//return the new UTMCoordinates object
    }
}
class UTMCoordinates { /*… */}

        明示的な変換が必要な場合は、implicit を Express に置き換えます。

4.プライベートアクセス修飾子

        派生クラスは、コンストラクターとデストラクターを除くすべての基本クラスのメンバーを継承します。このとき、プライベート メンバーには、宣言された型でのみアクセスできます。派生クラスは、基本クラスのプライベート メンバーにはアクセスできません。

public class PdaItem
{
    private string _Name;
    // ...
}

public class Contact : PdaItem
{
    // ...
}
public class Program
{
    public static void ChapterMain()
    {
        Contact contact = new Contact();
        // ERROR:  ‘PdaItem. _Name’ is inaccessible due to its protection level

        //contact._Name = "Inigo Montoya";  //uncomment this line and it will not compile
    }
}   

5. 保護されたアクセス修飾子

        基本クラスの保護されたメンバーには、基本クラスと派生チェーン内の他のクラスのみがアクセスできます。

public class Program
{
    public static void Main()
    {
        Contact contact = new Contact();
        contact.Name = "Inigo Montoya";
        // ERROR:  ‘PdaItem.ObjectKey’ is inaccessible due to its protection level
        //contact.ObjectKey = Guid.NewGuid(); //uncomment this line and it will not compile
     }
}
public class PdaItem   {  protected Guid ObjectKey { get; set; } }
public class Contact : PdaItem
{
    void Save()
    {
        // Instantiate a FileStream using <ObjectKey>.dat for the filename.
        FileStream stream = System.IO.File.OpenWrite(ObjectKey + ".dat");
    }
    void Load(PdaItem pdaItem)
    {
        // ERROR:  ‘pdaItem.ObjectKey’ is inaccessible due to its protection level
        //pdaItem.ObjectKey =...;
        Contact contact = pdaItem as Contact;
        if(contact != null)
            contact.ObjectKey = new Guid();//...; 
    }
    public string Name { get; set; }
}

6. 単一継承

  • 継承チェーンにおけるクラスの理論は無限です。ただし、C# は単一継承言語であるため、クラスを 2 つのクラスから直接派生することはできません。
  • まれに複数の継承クラス構造が必要になる場合、一般的な解決策は、あるクラスに別のクラスのインスタンスを含む集約を使用することです。

7. シールタイプ

        Sealed クラスには sealed 修飾子が必要なので、他のクラスはそのクラスから派生できなくなります。

public sealed class CommandLineParser
{
    // ...
}
// ERROR:  Sealed classes cannot be derived from
public sealed class DerivedCommandLineParser
    //: CommandLineParser //uncomment this line and it will not compile
{
    // ...
}

8. 仮想修飾子

  • C# では、インスタンス メソッドとプロパティのオーバーライドはサポートされていますが、フィールドや静的メンバーのオーバーライドはサポートされていません。オーバーライドするには、オーバーライドが許可されている各メンバーが基本クラスで仮想としてマークされている必要があります。
public class PdaItem
{
    public virtual string Name { get; set; }
}
public class Contact : PdaItem
{
    public override string Name
    {
        get {
            return $"{ FirstName } { LastName }";
        }
        set {
            string[] names = value.Split(' ‘);
            FirstName = names[0];
            LastName = names[1];
        }
    }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
  • 派生クラスに適用される C# の override キーワードは必須です。C# では暗黙的なオーバーライドは許可されません。オーバーライドされるためには、基本クラスと派生クラスのメンバーの署名が一貫している必要があります。

9. 新しい修飾子

        オーバーライドされたメソッドで override キーワードが使用されていない場合、コンパイラは警告メッセージを報告します。1 つは override キーワードを追加する方法で、もう 1 つは新しい修飾子を使用する方法です。

警告 CS0108: 'Program.DerivedClass.DisplayName()' は、継承されたメンバー 'Program.BaseClass.DisplayName()' を非表示にします。非表示にすることが意図されている場合は、 new キーワードを使用します。

警告 CS0114: 'Program.SuperSubDerivedClass.DisplayName()' は、継承されたメンバー 'Program.SubDerivedClass.DisplayName()' を非表示にします。現在のメンバーでその実装をオーバーライドするには、override キーワードを追加します。それ以外の場合は、新しいキーワードを追加します。

        新しい修飾子は、派生クラスの再宣言されたメンバーを基底クラスから非表示にします。このとき、継承チェーンが検索されて、新しい修飾子を使用するメンバーの前にメンバーが見つかり、そのメンバーが呼び出されます。次に例を示します。

public class Program{
    public class BaseClass{
        public void DisplayName() => Console.WriteLine("BaseClass");
    }
    public class DerivedClass : BaseClass{
        // Compiler WARNING: DisplayName() hides inherited  member. Use the new keyword if hiding was intended.
        public virtual void DisplayName() => Console.WriteLine("DerivedClass");
    }
    public class SubDerivedClass : DerivedClass{
        public override void DisplayName() =>Console.WriteLine("SubDerivedClass");
    }
    public class SuperSubDerivedClass : SubDerivedClass{
        public new void DisplayName()  => Console.WriteLine("SuperSubDerivedClass");
    }
    public static void Main()
    {
        SuperSubDerivedClass superSubDerivedClass = new SuperSubDerivedClass();
        SubDerivedClass subDerivedClass = superSubDerivedClass;
        DerivedClass derivedClass = superSubDerivedClass;
        BaseClass baseClass = superSubDerivedClass;
        superSubDerivedClass.DisplayName();
        subDerivedClass.DisplayName();
        derivedClass.DisplayName();
        baseClass.DisplayName();
    }
}

10. 封印された修飾子

        仮想メンバーをシールすることもできます。これにより、サブクラスが継承チェーンの上位にある基本クラスの仮想メンバーをオーバーライドできなくなります。次に例を示します。

class A
{
    public virtual void Method()
    {
    }
}
class B : A
{
     public override sealed void Method()
     {
     }
}
class C : B
{
     // ERROR:  Cannot override sealed members
     //public override void Method()
     //{
     //}
}

11.ベースメンバー

        メンバーをオーバーライドする場合、base キーワードを使用してメンバーの基本クラス バージョンを呼び出すことができます。Base は、現在のクラスの基本クラスを表します。

public class Address
{
    public string StreetAddress;
    public string City;
    public string State;
    public string Zip;
    public override string ToString()
    {
        return $"{ StreetAddress + NewLine }"
            + $"{ City }, { State }  { Zip }";
    }
}
public class InternationalAddress : Address {
    public string Country;
    public override string ToString() => base.ToString() +NewLine + Country;
}

12. コンストラクター

        派生クラスのオブジェクトを構築するときは、基本クラスのコンストラクターが最初に呼び出されます。基本クラスにデフォルトのコンストラクターがない場合は、派生クラスのコンストラクターで基本クラスのコンストラクターを明示的に呼び出す必要があります。次に例を示します。

public class PdaItem
{
    public PdaItem(string name)
    {
        Name = name;
    }
    public string Name { get; set; }
}
public class Contact : PdaItem
{
    public Contact(string name) :
        base(name)
    {
        Name = name;
    }
    public new string Name { get; set; }
    // ...
}

13. 抽象クラス

        抽象クラスは派生のみを行うクラスであり、抽象クラスはインスタンス化できず、抽象クラスから派生したクラスのみインスタンス化できます。抽象的ではなくインスタンス化できるクラスは、具象クラスと呼ばれます。

        抽象クラスは抽象エンティティを表します。クラスが抽象クラスから正常に派生するには、抽象クラスの抽象メソッドに具体的な実装を提供する必要があります。C# では、クラス定義に abstract 修飾子を追加する必要があります。

public abstract class PdaItem
{
    public PdaItem(string name)
    {
        Name = name;
    }
    public virtual string Name { get; set; }
}
public class Program
{
    public static void Main()
    {
        PdaItem item;
        // ERROR:  Cannot create an instance of the abstract class
        //item = new PdaItem("Inigo Montoya"); //uncomment this line and it will not compile
    }
}

14. 抽象メンバー

        非インスタンス化可能性は抽象クラスの小さな機能にすぎません。その主な機能は、抽象メンバーが含まれていることです。抽象メンバーは、実装を持たないメソッドまたはプロパティです。その役割は、すべての派生クラスに強制的に実装することです。

public abstract class PdaItem{
    public PdaItem(string name)
    {
        Name = name;
    }
    public virtual string Name { get; set; }
    public abstract string GetSummary();
}

public class Contact : PdaItem{
    public Contact(string name) : base(name) {}
    public override string Name
    {
        get{                
            return $"{ FirstName } { LastName }";
        }
        set{
            string[] names = value.Split(' ‘);
            FirstName = names[0];
            LastName = names[1];
        }
    }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address { get; set; }
    public override string GetSummary() =>$"FirstName: { FirstName + NewLine }"
        + $"LastName: { LastName + NewLine }" + $"Address: { Address + NewLine }";
}

public class Appointment : PdaItem
{
    public Appointment(string name) :
        base(name)
    {
        Name = name;
    }

    public DateTime StartDateTime { get; set; }
    public DateTime EndDateTime { get; set; }
    public string Location { get; set; }

    // ...
    public override string GetSummary()
    {
        return $"Subject: { Name + NewLine }"
            + $"Start: { StartDateTime + NewLine }"
            + $"End: { EndDateTime + NewLine }"
            + $"Location: { Location }";
    }
}

        異なるクラスで同じ署名を持つメンバーの異なる実装はポリモーフィズムと呼ばれます。抽象メンバーはポリモーフィズムを実現する手段です。

public class Program{
    public static void Main()
    {
        PdaItem[] pda = new PdaItem[3];
        Contact contact = new Contact("Sherlock Holmes");
        contact.Address = "221B Baker Street, London, England";
        pda[0] = contact;
        Appointment appointment = new Appointment("Soccer tournament");
        appointment.StartDateTime = new DateTime(2008, 7, 18);
        appointment.EndDateTime = new DateTime(2008, 7, 19);
        appointment.Location = "Estádio da Machava";
        pda[1] = appointment;
        contact = new Contact("Anne Frank");
        contact.Address = "Apt 56B, Whitehaven Mansions, Sandhurst Sq, London";
        pda[2] = contact;
        List(pda);
    }
    public static void List(PdaItem[] items)
    {
        // Implemented using polymorphism. The derived type knows the specifics of implementing GetSummary().
        foreach(PdaItem item in items)
        {
            Console.WriteLine("________");
            Console.WriteLine(item.GetSummary());
        }
    }
}

15、システム.オブジェクト

        カスタム型であってもシステム組み込み型であっても、オブジェクトは最終的に Sytem.Object から派生します。したがって、次のメソッドが定義されています。

16. is 演算子を使用して基本型を検証する

        C# では、継承チェーン内での下向きキャストが可能です。C# には、基本型を決定するための is 演算子が用意されています。データ項目が特定のタイプであるかどうかを検証できます。

public class AnObject
{
    public static void Save(object data)
    {
        if(data is string)
        {
            data = Encrypt((string)data);
        }
        // ...
    }

    private static object Encrypt(string data)
    {
        return null;
    }
}

17. as 演算子を使用して変換します

        as 演算子は、オブジェクトを特定の型に変換しようとします。オブジェクトが変換できない場合、as 演算子は null を返します。したがって、変換が失敗しても例外はスローされません。

class Program
{
    static object Print(IDocument document)
    {
        if(document != null)
        {
            // Print document...
        }
        return null;
    }
    static void  Main()
    {
        object data = new object();
        // ...
        Print(data as Document);
    }
}
internal class Document : IDocument
{
}
internal interface IDocument
{
}

2. インターフェース

1。概要

  • 抽象クラスとは異なり、インターフェイスは提供されるサービスから実装の詳細を完全に分離できます。インターフェイスの主な特徴は、インターフェイスに実装もデータも含まれていないことです。
  • 慣例により、インターフェイス名には、接頭辞として大文字の I を付けたパスカル文字スタイルが使用されます。            
    • インターフェース Ixxxx { //... }  
  • インターフェイスによって宣言されたメンバーは、インターフェイスを実装する型でアクセス可能でなければならないメンバーを記述します。
interface IFileCompression
{
    void Compress(string targetFileName, string[] fileList);
    void Uncompress(
        string compressedFileName, string expandDirectoryName);
}
  • インターフェイスが特定のデータを含む派生クラスを必要とする場合、フィールドの代わりにプロパティが宣言されます。
interface IListable {
    string[] ColumnValues // Return the value of each column in the row.
    {
        get;
    }
}
public abstract class PdaItem{
    public PdaItem(string name) {
        Name = name;
    }
    public virtual string Name { get; set; }
}
class Contact : PdaItem, Ilistable{
    public Contact(string firstName, string lastName, string address, string phone)
	    : base(null)
    {
        FirstName = firstName; 
        LastName = lastName; 
        Address = address;
        Phone = phone;
    }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address { get; set; }
    public string Phone { get; set; }
    public string[] ColumnValues {
        get {
            return new string[] { FirstName, LastName, Phone, Address };
        }
    }
    public static string[] Headers {
        get { return new string[] {"First Name", "Last Name ", "Phone ", "Address" };
    }
}

2. インターフェースの実装

  • インターフェイスを実装するクラスの宣言は、基本クラスからの派生と似ています。実装するインターフェイスと基本クラスの名前はカンマで区切られ、基本クラスが最初でインターフェイスは任意の順序になります。クラスは複数のインターフェイスを実装できますが、基本クラスから直接派生することしかできません。例えば:
public class Contact : PdaItem, IListable, IComparable
{
    public Contact(string name) : base(name) {}
    #region IComparable Members
    public int CompareTo(object obj)
    {
        int result;
        Contact contact = obj as Contact;、
        if(obj == null) 
            result = 1; // This instance is greater than obj. 
        else if(obj.GetType() != typeof(Contact)) 
            throw new ArgumentException($"The parameter is not a of type { nameof(Contact) }", nameof(obj));
        else if(Contact.ReferenceEquals(this, obj))
            result = 0;
        else {
                result = LastName.CompareTo(contact.LastName);
                if(result == 0)
                    result = FirstName.CompareTo(contact.FirstName);
            }
            return result;
        }
        #endregion
        string[] IListable.ColumnValues{
        get {
            return new string[]  {FirstName, LastName, Phone, Address};
        }
    }
    protected string LastName { get; set; }
    protected string FirstName { get; set; }
    protected string Phone { get; set; }
    protected string Address { get; set; }
}
  • クラスが実装するインターフェイスを宣言したら、インターフェイスのすべてのメンバーを実装する必要があります。抽象クラスを使用すると、インターフェイス メンバーの抽象実装を提供できます。  
  • インターフェイスのもう 1 つの特徴は、インスタンス化できないことです。new を使用してインターフェイスを作成することはできないため、インターフェイスにはコンストラクターやデストラクターがありません。インターフェイス インスタンスは、インターフェイスを実装する型をインスタンス化することによってのみ使用できます。インターフェイスに静的メンバーを含めることはできません  
  • インターフェイス メンバーを型に実装するには、明示的と暗黙的の 2 つの方法があります。

3. 表示メンバーの実装

        明示的に宣言されたインターフェイス メンバーは、メンバー名の前にインターフェイス プレフィックスを追加する必要があります。次に例を示します。

public class Contact : PdaItem, IListable, IComparable {
    //….
    string[] IListable.ColumnValues{
        get {
            return new string[]  {FirstName, LastName, Phone, Address};
        }
    }
}

        明示的に実装されたインターフェイスはインターフェイス自体を通じてのみ呼び出すことができるため、オブジェクトをインターフェイスにキャストする必要があります。明示的に実装されたインターフェイス メンバーはインターフェイスに直接関連付けられているため、virtual、override、または public で変更することはできません。例えば:

public class Program
{
    public static void Main()
    {
         string[] values;
         Contact contact1, contact2 = null;
         // ERROR:  Unable to call ColumnValues() directly on a contact.
         // values = contact1.ColumnValues;
         // First cast to IListable.
         values = ((IListable)contact2).ColumnValues;
         // ...
    }
}

4. 暗黙的なメンバーの実装

        暗黙的な実装では、メンバーがパブリックであることと、署名がインターフェイス メンバーの署名と一致することのみが必要です。インターフェイス メンバーの実装には、override キーワードや、メンバーがインターフェイスに関連付けられていることを示すその他のインジケーターは必要ありません。  

        メンバーは他のクラス メンバーと同様に宣言されるため、暗黙的に実装されたメンバーは他のクラス メンバーと同様に直接呼び出すことができます。結果 = contact1.CompareTo(contact2);

        暗黙的な実装では、明示的な実装では許可されていない多くの修飾子が必須またはオプションです。例: 暗黙的な実装はパブリックである必要があります。また、仮想を選択することもできます。これは、派生クラスをオーバーライドできるかどうかによって異なります。virtual を削除すると、メンバーが封印されます。

5. 明示的なインターフェイス実装と暗黙的なインターフェイス実装の比較

明示的または暗黙的な実装を選択するための基本原則:

  • メンバーがクラスの補助メンバーである場合は、クラスの直接表示可能なメンバーとして設計する必要があります。  
  • 実装クラスでインターフェイス メンバーの目的が明確でない場合は、明示的なメンバーを検討できます。  
  • 明示的なインターフェイス メンバーは、名前付き要素を型の宣言スペースに追加しません。したがって、型に競合する可能性のあるメンバーがあるとします。もう 1 つは明示的なインターフェイス メンバーであり、それと同じ名前を持つことができます。

6. インターフェースの継承

  • インターフェイスは別のインターフェイスから派生でき、派生インターフェイスは「基本インターフェイス」のすべてのメンバーを継承します。例えば:
interface IReadableSettingsProvider
{
    string GetSetting(string name, string defaultValue);
}
interface ISettingsProvider : IReadableSettingsProvider
{
    void SetSetting(string name, string value);
}
class FileSettingsProvider : ISettingsProvider{
    public void SetSetting(string name, string value)  { /*.. */ }
    public string GetSetting(string name, string defaultValue)
    {
        return name + defaultValue; // just returning this for the example
    }
}
  • GetSetting が明示的に実装されている場合は、IReadableSettingsProvider を渡す必要があります。
  • クラスによって実装されるインターフェイスが基本インターフェイスから派生した場合でも、クラスはこれら 2 つのインターフェイスを実装することを宣言できます。例えば:
interface IReadableSettingsProvider
{
    string GetSetting(string name, string defaultValue);
}
interface ISettingsProvider : IReadableSettingsProvider
{
    void SetSetting(string name, string value);
}
class FileSettingsProvider : ISettingsProvider, IReadableSettingsProvider{
    public void SetSetting(string name, string value)  { /*.. */ }
    public string GetSetting(string name, string defaultValue)
    {
        return name + defaultValue; // just returning this for the example
    }
}

7. マルチインターフェースの継承

インターフェイスは複数のインターフェイスから継承することもでき、その構文はクラスの継承構文および実装構文と一致します。

interface IReadableSettingsProvider
{
    string GetSetting(string name, string defaultValue);
}
interface IWriteableSettingsProvider
{
    void SetSetting(string name, string value);
}
interface ISettingsProvider : IReadableSettingsProvider,
    IWriteableSettingsProvider
{
}

8. インターフェース上の拡張メソッド

        拡張メソッドの重要な特徴は、クラスに作用するだけでなく、インターフェースにも作用できることです。構文はクラスの場合と同じです。メソッドの最初のパラメータは拡張するインターフェイスであり、このパラメータにはこの修飾子を追加する必要があります。

class Program
{
    public static void Main()
    {
        Contact[] contacts = new Contact[6];
        contacts[0] = new Contact("Dick", "Traci", "123 Main St., Spokane, WA 99037", "123-123-1234");
        contacts.List(Contact.Headers); // Classes are implicitly converted totheir supported interfaces.
        Console.WriteLine();
        //…..
    }
}
static class Listable
{
    public static void List( this IListable[] items, string[] headers)
    {
        //....
    }
}

9. インターフェースを介した多重継承の実装

ただし、クラス関数は基本クラスから派生します。ただし、インターフェイスはいくつでも実装できます

interface IPerson {
    string FirstName {get; set;   }
    string LastName{get;  set; }
}
public class Person : IPerson {
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
public class Contact : PdaItem, IPerson {
public Contact(string name)
    : base(name) {}
    private Person Person
    {
        get { return _Person; }
        set { _Person = value; }
    }
    private Person _Person;
    public string FirstName
    {
        get { return _Person.FirstName; }
        set { _Person.FirstName = value; }
    }        
    public string LastName {
        get { return _Person.LastName; }
        set { _Person.LastName = value; }
    }
}

3. インターフェースとクラスの比較

Guess you like

Origin blog.csdn.net/weixin_44906102/article/details/132843310