C# .NET 的面向对象编程

作者 : 慕源网 本文共14961个字,预计阅读时间需要38分钟 发布时间: 2021-11-8 共217人阅读

面向对象编程 (OOP) 是 .NET 框架的核心成分。OOP 是如此重要,以至于在踏上 .NET 之路之前,您必须了解它的基本原理和术语才能编写一个简单的程序。OOP 背后的基本思想是将数据和操作该数据的方法组合成一个单元;这样的单位称为对象。所有 OOP 语言都提供了帮助您实现面向对象模型的机制。它们是封装、继承、多态性和可重用性。现在让我们简要地看看这些概念。

C# .NET 的面向对象编程

本章涵盖以下内容:

  1. OOP 概述
  2. 类和对象
  3. 构造函数和析构函数
  4. 函数重载
  5. 封装
  6. 继承
  7. Interface
  8. 多态性

封装

封装将代码和它操作的数据绑定在一起,并保护它们免受外部干扰和误用。封装是一种保护性容器,可防止代码和数据被容器外定义的其他代码访问。

继承

继承是一个对象获取另一个对象属性的过程。类型派生自基类型,采用所有基类型成员字段和函数。当您需要向现有类型添加功能时,继承最有用。例如,所有 .NET 类都继承自 System.Object 类,因此类可以包含新功能以及使用现有对象的类函数和属性。

多态性

多态性是一种特性,它允许将一个接口用于一般的动作类。这个概念通常表示为“一个界面,多个动作”。具体行动由具体情况决定。

可重用性

一旦编写、创建和调试了一个类,就可以将它分发给其他程序员以在他们自己的程序中使用。这称为可重用性,或在 .NET 术语中,此概念称为组件或 DLL。然而,在 OOP 中,继承为可重用性的概念提供了重要的扩展。程序员可以使用现有的类,而不用修改它,向它添加额外的功能。

简单的“Hello World”C# 程序

这个简单的一类控制台“Hello world”程序在本文和以后的几篇文章中展示了许多基本概念。

using System;

namespace oops
{

    //class definition
    public class SimpleHelloWorld
    {
         //Entry point of the program
        static void Main(string[] args)
        {
            //print Hello world"
            Console.WriteLine("Hello World!");
        }
    }
}

所以 SimpleHelloWorld 是包含 Main() 方法的类的名称。在第 1 行, using 指令向编译器指示此源文件引用在 System 命名空间中声明的类和构造。带有 public 关键字的第 6 行表示其他应用程序或组件的程序可访问性范围。

在第 7 行,出现了一个左花括号 (“{“),它表示 SimpleHelloWorld 类主体的开始。一切都属于该类,例如字段、属性和方法出现在类主体中的左括号和右括号之间。Main() 方法的目的是为应用程序执行提供一个入口点。

Main() 方法中的 static 关键字声明该方法将在不实例化类的情况下执行。

编译程序

您可以将 C# 程序编译为程序集或模块。如果程序有一个包含 Main() 方法的类,那么它可以直接编译成程序集。该文件具有“.exe”扩展名。没有 Main() 方法的程序可以编译成一个模块,如下所示:

csc /target:module “program name”

然后,您可以通过 F9 或简单地针对源文件运行 C# 命令行编译器 (csc.exe) 来编译此程序,如下所示:

csc oops.cs

类和对象

类是特殊类型的模板,您可以从中创建对象。每个对象都包含数据和操作和访问该数据的方法。该类定义了该类的每个对象可以包含的数据和功能。

类声明由类头和类体组成。类头包括属性、修饰符和 class 关键字。类体封装了类的成员,即数据成员和成员函数。类声明的语法如下:

属性可访问性修饰符类标识符:baselist { body }

属性为类提供额外的上下文,如形容词;例如 Serializable 属性。可访问性是类的可见性。类的默认可访问性是内部的。Private 是类成员的默认可访问性。下表列出了可访问性关键字;

关键词 描述
public 公共类在当前和引用程序集中可见。
private 在当前类中可见。
protected 在当前类和派生类中可见。
Internal 可见内部包含组件。
Internal protected  可见内部包含当前类的程序集和后代。

修饰符细化了类的声明。表中定义的所有修饰符列表如下;

修饰符 描述
sealed 派生类不能继承类。
static 类只包含静态成员。
unsafe 具有一些不安全构造的类喜欢指针。
Abstract 如果类是抽象类,则不会创建类的实例。

基表是继承的类。默认情况下,类继承自 System.Object 类型。一个类可以继承和实现多个接口,但不支持多重继承。

创建类的分步教程

  1. 从开始菜单打开 Visual Studio 2010。
  2. 转到“文件”>“新建”>“项目…”,在右侧窗格中选择“控制台应用程序”并为项目提供名称“oops”。
  3. 然后在解决方案资源管理器中,您会注意到一些自动创建的文件,C# .NET 的面向对象编程
  4. 您还可以在创建的默认 program.cs 文件中编写自己的代码,但创建新类是一种很好的编程实践。

要添加新类,请在解决方案资源管理器中右键单击项目名称 (oops),然后单击“添加”>“类”。将名称命名为“customer”类,如下所示;C# .NET 的面向对象编程

当您打开 customer.cs 类时。你会发现一些默认生成的代码如下,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace oops
{
    class customer
    {
    }
}

注意

C# 控制台应用程序项目必须需要已在程序类中生成的单个入口点 Main() 函数。例如,如果您添加一个新的客户类并希望在此处定义一个或多个 Main() 入口点,那么 .NET 将抛出多个入口点的错误。因此建议从解决方案中删除或排除 program.cs 文件。

因此,在此示例中,客户类定义了 CustID、Name 和 Address 等字段以保存有关特定客户的信息。它还可能定义一些对存储在这些字段中的数据起作用的功能。

using System;

namespace oops
{
    class customer
    {
        // Member Variables
        public int CustID;
        public string Name;
        public string Address;

        //constuctor for initializing fields
        customer()
        {
            CustID=1101;
            Name="Tom";
            Address="USA";
        }

        //method for displaying customer records (functionality)
        public void displayData()
        {
            Console.WriteLine("Customer="+CustID);
            Console.WriteLine("Name="+Name);
            Console.WriteLine("Address="+Address);
        }
       // Code for entry point
    }
}

在第 9 行,我们定义了用于初始化类成员字段的客户类的构造函数。构造函数是一个特殊的函数,在创建(实例化)客户类对象时会自动调用。在第 11 行,我们通过创建用户定义的方法 displayData() 将这些字段打印到控制台。 

然后,您可以实例化此类的一个对象来表示一个特定客户,为该实例设置字段值并使用其功能,如下所示:   

class customer
{
    // class members code

     //Entry point
    static void Main(string[] args)
    {
        // object instantiation
        customer obj = new customer();

        //Method calling
        obj.displayData();

         //fields calling
        Console.WriteLine(obj.CustID);
        Console.WriteLine(obj.Name);
        Console.WriteLine(obj.Address);
    }
}

在这里,您使用关键字 new 来声明客户类实例。这个关键字创建对象并初始化它。当您创建客户类的对象时,.NET 框架 IDE 提供一种称为 Intellisense 的特殊功能,该功能自动提供对所有类成员字段和函数的访问。此功能在“.”时调用。运算符放在对象之后,如下所示;

 

C# .NET 的面向对象编程

图 1.1 智能感知功能

通常,随着程序规模的增长和代码变得更加复杂,Intellisense 功能通过显示所有成员字段、属性和函数来增加程序员的便利性。

多类声明

有时情况需要在单个命名空间中声明多个类。因此,在这种情况下,不必向解决方案添加单独的类,而是可以将新类附加到现有的 program.cs 或另一个类中,如下所示; 

using System;

namespace oops
{
    class Program
    {

        public void MainFunction()
        {
          Console.WriteLine("Main class");
        }
        static void Main(string[] args)
        {
            //main class instance
            Program obj = new Program();
            obj.MainFunction();

            //other class instace
            demo dObj=new demo();
            dObj.addition();
        }
    }

    class demo
    {
        int x = 10;
        int y = 20;
        int z;

        public void addition()
        {
            z = x + y;
            Console.WriteLine("other class in Namespace");
            Console.WriteLine(z);
        }
    }
}

在这个例子中,我们在 program.cs 文件的第 12 行创建了一个额外的类“演示”,最后我们在第 6 到 11 行的 Main() 条目中使用程序类实例化演示类。所以它不会我们在单个程序集中定义多少个类无关紧要。

部分类

通常,一个类将完全驻留在单个文件中。然而,在多个开发人员需要访问同一个类的情况下,将类放在多个文件中可能是有益的。部分关键字允许一个类跨越多个源文件。编译时,部分类型的元素组合成一个程序集。

定义分部类有一些规则,如下所示;

  • 部分类型必须具有相同的可访问性。
  • 每个部分类型前面都有“partial”关键字。
  • 如果部分类型是密封的或抽象的,那么整个类将是密封的和抽象的。

在以下示例中,我们将添加两个文件 partialPart1.cs 和 partialPart2.cs,并在两个类中声明一个分部类 partialclassDemo。

partialPart1.cs

using System;

namespace oops
{
    public partial class partialclassDemo
    {
        public void method1()
        {
            Console.WriteLine("method from part1 class");
        }
    }
}

partialPart2.cs

using System;

namespace oops
{
    public partial class partialclassDemo
    {
        public void method2()
        {
            Console.WriteLine("method from part2 class");
        }
    }
}

最后,我们在 program.cs 文件中创建了一个 partialclassDemo 的实例,如下所示:

Program.cs

using System;

namespace oops
{
    class Program
    {
        static void Main(string[] args)
        {
            //partial class instance
            partialclassDemo obj = new partialclassDemo();
            obj.method1();
            obj.method2();
        }
    }
}

静态类

使用“static”关键字声明静态类。如果类声明为静态,则编译器永远不会创建类的实例。所有成员字段、属性和函数都必须声明为静态,并且它们可以通过类名直接访问,而不是通过类实例对象访问。

using System;

namespace oops
{
    static class staticDemo
    {
        //static fields
        static int x = 10, y;

        //static method
        static void calcute()
        {
            y = x * x;
            Console.WriteLine(y);
        }
        static void Main(string[] args)
        {
            //function calling directly
            staticDemo.calcute();
        }
    }
}

创建和访问类组件库

.NET 提供了创建基础应用程序的库(组件)而不是可执行文件(“.exe”)的功能。相反,库项目的最终构建版本将是“.DLL”,可以从其他外部应用程序引用以公开其全部功能。

分步教程

首先创建一个基于类库的应用程序,

C# .NET 的面向对象编程

然后我们正在实现一个数学类库,它负责计算平方根和两个数字的加法:

using System;

namespace LibraryUtil
{
    public class MathLib
    {
        public MathLib() { }

        public void calculareSum(int x, int y)
        {
            int z = x + y;
            Console.WriteLine(z);
        }

        public void calculareSqrt(double x)
        {
            double z = Math.Sqrt(x);
            Console.WriteLine(z);
        }
    }
}

构建此代码,您会注意到在应用程序的根目录(路径 = D:\temp\LibraryUtil\LibraryUtil\bin\Debug\LibraryUtil.dll)中创建了一个 DLL 文件,而不是一个可执行文件

现在创建另一个基于控制台的应用程序,您可以在其中使用所有类库的功能。

然后你必须添加类库dll文件引用才能访问库dll中声明的类。(右键单击“引用”,然后“添加引用”,然后选择 dll 文件的路径。)

当您添加类库引用时,您会注意到在解决方案资源管理器中添加了一个新的 LibraryUtil,如下所示;

C# .NET 的面向对象编程

现在在控制台应用程序中添加类库文件的命名空间,并创建在库中声明的类的实例,如下所示;

using System;
using LibraryUtil; // add library namespace
namespace oops
{
    public class LibraryClass
    {
        static void Main()
        {
            //library class instance
            MathLib obj = new MathLib();

            //method populate
            obj.calculareSum(2, 5);
            obj.calculareSqrt(25);
        }
    }
}

最后运行应用程序。

构造函数和析构函数

构造函数是用于初始化字段的专用函数。构造函数与类同名。实例构造函数使用 new 运算符调用,并且不能以与其他成员函数相同的方式调用。有一些与构造函数有关的重要规则,如下所示;

  • 没有构造函数的类有一个称为默认构造函数的隐式构造函数,它是无参数的。默认构造函数为字段分配默认值。
  • 公共构造函数允许在当前程序集或引用程序集中创建对象。
  • 构造函数上只允许使用 extern 修饰符。
  • 构造函数返回 void 但没有显式声明的返回类型。
  • 构造函数可以有零个或多个参数。
  • 类可以具有默认、参数或两者形式的多个构造函数。

以下示例显示了客户类的一个构造函数。

using System;
namespace oops
{
    class customer
    {
        // Member Variables
        public string Name;

        //constuctor for initializing fields
        public customer(string fname, string lname)
        {
            Name= fname +" "+ lname;
        }
        //method for displaying customer records
        public void AppendData()
        {
            Console.WriteLine(Name);
        }
         //Entry point
        static void Main(string[] args)
        {
            // object instantiation
            customer obj = new customer("Barack", "Obama");

            //Method calling
            obj.AppendData();
        }
    }
}

注意

执行新语句的那一刻,将调用默认构造函数。

静态构造函数

构造函数可以是静态的。您创建一个静态构造函数来初始化静态字段。静态构造函数不会用 new 语句显式调用。它们在类第一次被引用时被调用。静态构造函数有一些限制,如下所示;

  • 静态构造函数是无参数的。
  • 静态构造函数不能重载。
  • 没有为静态构造函数指定可访问性。

在以下示例中,客户类有一个静态构造函数,用于初始化静态字段,当在 Main() 的第 26 行引用该类时,将调用此构造函数,如下所示:

using System;
namespace oops
{
    class customer
    {
        // Member Variables
        static private int x;

        //constuctor for static initializing fields
        static customer()
        {
            x = 10;
        }
        //method for get  static field
        static public void getData()
        {
            Console.WriteLine(x);
        }
         //Entry point
        static void Main(string[] args)
        {
           //static Method calling
            customer.getData();
        }
    }
}

析构函数

析构函数方法的目的是删除未使用的对象和资源。析构函数不是在源代码中直接调用,而是在垃圾收集期间调用。垃圾收集是不确定的。在不确定的时刻调用析构函数。更准确地说,程序员无法控制它的执行;而是由 Finalize() 方法调用。与构造函数一样,析构函数与类同名,只是析构函数以波浪号 (~) 为前缀。析构函数有一些限制,如下所示;

  • 析构函数是无参数的。
  • 析构函数不能重载。
  • 析构函数不是继承的。
  • 析构函数可能会导致性能和效率影响。

下面实现了一个析构函数和 dispose 方法。首先,我们通过构造函数初始化字段,对该数据进行一些计算并将其显示到控制台。但是在第 9 行,我们正在实现调用 Dispose() 方法以释放所有资源的析构函数。

using System;
namespace oops
{
    class customer
    {
        // Member Variables
        public int x, y;
        //constuctor for  initializing fields
        customer()
        {
            Console.WriteLine("Fields inititalized");
            x = 10;
        }
        //method for get field
        public void getData()
        {
            y = x * x;
            Console.WriteLine(y);
        }
        //method to release resource explicitly
        public void Dispose()
        {
            Console.WriteLine("Fields cleaned");
            x = 0;
            y = 0;
        }
        //destructor
        ~customer()
        {
            Dispose();
        }
         //Entry point
        static void Main(string[] args)
        {
            //instance created
            customer obj = new customer();

            obj.getData();

        }
    }
}

在创建实例时的第 12 行,字段被初始化,但不必同时调用析构函数。它的调用依赖于垃圾收集。如果你想看到析构函数被调用,那么在第 10 行放置一个断点(按 F9)并编译应用程序。CLR 在程序结束时通过使用黄色突出显示第 10 行来指示其执行。

函数重载

函数重载允许在一个类中多个实现相同的函数。重载方法共享相同的名称但具有唯一的签名。参数的数量、参数的类型或两者都必须不同。不能仅根据不同的返回类型重载函数。

using System;
namespace oops
{
    class funOverload
    {
        public string name;

        //overloaded functions
        public void setName(string last)
        {
            name = last;
        }

        public void setName(string first, string last)
        {
            name = first + "" + last;
        }

        public void setName(string first, string middle, string last)
        {
            name = first + "" + middle + "" + last;
        }

        //Entry point
        static void Main(string[] args)
        {
            funOverload obj = new funOverload();

            obj.setName("barack");
            obj.setName("barack "," obama ");
            obj.setName("barack ","hussian","obama");

        }
    }
}

在第 3、4 和 5 行,我们定义了三个名称相同但参数不同的方法。在 Main() 中,当您创建类的实例并在第 7、8 和 9 行通过 obj 调用函数 setName() 时,intellisense 将自动显示三个签名。

封装

封装是将代码和它操作的数据绑定在一起的机制,并保护两者免受外部干扰和误用。在 OOP 中,代码和数据可以以创建自包含框的方式组合。当代码和数据以这种方式链接在一起时,就创建了一个对象并存在封装。

在一个对象中,代码、数据或两者对于该对象来说可能是私有的或公开的。私有代码只能被对象的另一部分知道和访问,即私有代码或数据可能无法被存在于对象外部的一段程序访问。当代码和数据是公开的时,程序的其他部分可能会访问它,即使它是在对象中定义的。

using System;
namespace oops
{
    class Encapsulation
    {
        /// <summary>
        /// Every member Variable and Function of the class are bind
        /// with the Encapsulation class object only and safe with
        /// the outside inference
        /// </summary>

        // Encapsulation Begin
        int x;

        //class constructor
        public Encapsulation(int iX)
        {
            this.x = iX;
        }

        //calculating the square
        public void MySquare()
        {
            int Calc = x * x;
            Console.WriteLine(Calc);
        }

        // End of Encapsulation

         //Entry point
        static void Main(string[] args)
        {
            //instance created
            customer obj = new customer(20);

            obj. MySquare();

        }

    }
}

继承

继承是一个对象可以获取另一个对象的属性的过程。继承是一种“是一种”关系,它支持分类的概念,其中一个对象只需要定义那些使其在类中独一无二的品质。继承涉及基类和派生类。派生类继承自基类,还可以覆盖继承的成员以及添加新成员以扩展基类。

基类型表示泛化,而派生类型表示实例的规范。例如Employees 可以有多种类型,例如hourly、salaryed 和temporary,那么在这种情况下Employees 是一般基类,而hourly、salaryed 和temporary 雇员是专门的派生类。

类可以从单个类和一个或多个接口继承。从类继承时,派生类会继承包括基类代码在内的成员。要记住的重点是构造函数和析构函数不是从基类继承的。

继承的语法如下;

类派生类:baseClass、Iterface1、Interface2 { body }

例如,我们定义了两个类,Father 和 Child。你注意到在第 7 行,我们通过使用冒号 (:); 来实现继承;此时所有属于父类的属性都可以自动被子类访问。

C# .NET 的面向对象编程

using System;
namespace oops
{
    //Base Class
    public class Father
    {
        public void FatherMethod()
        {
            Console.WriteLine("this property belong to Father");
        }
    }

    //Derived class
    public class Child : Father
    {
        public void ChildMethod()
        {
            Console.WriteLine("this property belong to Child");
        }
    }
    class Inheritance
    {
        //Entry point
        static void Main(string[] args)
        {

            Father fObj = new Father();
            fObj.FatherMethod();

            //Here Child object can access both class methods
            Child cObj = new Child();
            cObj.FatherMethod();
            cObj.ChildMethod();
        }
    }
}

在第 11 行,Intellisense 仅显示父类函数,但在第 15 到 16 行,子类对象能够访问两个类方法,如下所示。

我们可以在 VB.Net 语言或其他 .NET 支持的语言中创建一个类,并且可以在 C# .Net 类中继承它们,反之亦然。但是在 C++ 或其他非托管环境中开发的类不能在 .NET 中继承。

注意

.NET 不支持跨语言和多重继承。

无障碍

可访问性将成员的可见性设置为外部程序集或派生类型。下表描述了成员可访问性;

修饰符 外部程序集 派生类
private No No
public Yes Yes
protected No No
internal Yes ( 仅此程序集) Yes ( 仅此程序集)
internal protected Yes ( 仅此程序集) Yes

继承中的构造函数

派生类中不会继承基类中的构造函数。派生类具有基础部分和派生部分。基部分初始化基部分,派生类的构造函数初始化派生部分。

以下是继承中构造函数的语法;

可访问性修饰符 classname(parameterlist1) : base(parameterlist2) { body }

所以 base 关键字是指基类构造函数,而 parameterlist2 决定调用哪个重载的基类构造函数。

在下面的例子中,Child 类的构造函数调用父类的单参数构造函数;

using System;
namespace oops
{
    //Base Class
    public class Father
    {

        //constructor
        public Father()
        {
            Console.WriteLine("Father class constructor");
        }

        public void FatherMethod()
        {
            Console.WriteLine("this property belong to Father");
        }
    }

    //Derived class
    public class Child : Father
    {
        public Child()
            : base()
        {
            Console.WriteLine("child class constructor");
        }
        public void ChildMethod()
        {
            Console.WriteLine("this property belong to Child");
        }
    }
    class Inheritance
    {
        //Entry point
        static void Main(string[] args)
        {
            //Here Child object can access both class methods
            Child cObj = new Child();
            cObj.FatherMethod();
            cObj.ChildMethod();
            Console.ReadKey();
        }
    }
}

在第 4 行,我们定义了父类的基构造函数,在派生类 Child 中,在第 8 行,我们通过 base 关键字显式初始化它。如果我们在基类构造函数中传递任何参数,那么我们必须在子类构造函数的基块中提供它们。

Virtual Methods

通过将基类函数声明为虚拟函数,您可以在任何派生类中覆盖该函数。虚函数背后的想法是根据需要在派生类中重新定义基类方法的实现。如果一个方法在基类中是虚拟的,那么我们必须在派生类中提供 override 关键字。成员字段和静态函数都不能声明为虚拟的。

using System;
namespace oops
{
    class myBase
    {
        //virtual function
        public virtual void VirtualMethod()
        {
            Console.WriteLine("virtual method defined in the base class");
        }
    }

    class myDerived : myBase
    {
        // redifing the implementation of base class method
        public override void VirtualMethod()
        {
            Console.WriteLine("virtual method defined in the Derive class");
        }
    }
    class virtualClass
    {
        static void Main(string[] args)
        {
            // class instance
            new myDerived().VirtualMethod();
            Console.ReadKey();
        }
    }
}

隐藏方法

如果在基类和派生类中都声明了具有相同签名的方法,但这些方法并未分别声明为虚拟和重写,则称派生类版本隐藏了基类版本。在大多数情况下,您希望覆盖方法而不是隐藏它们。否则 .NET 会自动生成警告。

在下面的示例中,我们在 myBase 类中定义了一个 VirutalMethod(),但在派生类中没有覆盖它,因此在这种情况下编译器将生成警告。编译器会假设您隐藏了基类方法。因此,为了克服这个问题,如果您在派生类方法中添加 new 关键字前缀,那么编译器将更喜欢派生版本最高的方法。您仍然可以使用 base 关键字访问派生类中的基类方法。

using System;
namespace oops
{
    class myBase
    {
        //virtual function
        public virtual void VirtualMethod()
        {
            Console.WriteLine("virtual method defined in the base class");
        }
    }

    class myDerived : myBase
    {
        // hiding the implementation of base class method
        public new void VirtualMethod()
        {
            Console.WriteLine("virtual method defined in the Derive class");

            //still access the base class method
            base.VirtualMethod();
        }
    }
    class virtualClass
    {
        static void Main(string[] args)
        {
            // class instance
            new myDerived().VirtualMethod();
            Console.ReadKey();
        }
    }
}

抽象类

C# 允许使用 abstract 关键字将类和函数声明为抽象。您不能创建抽象类的实例。抽象成员有签名但没有函数体,它们必须在任何非抽象派生类中被覆盖。抽象类的存在主要是为了继承。成员函数、属性和索引器可以是抽象的。具有一个或多个抽象成员的类也必须是抽象的。静态成员不能是抽象的。

在这个例子中,我们使用一个没有实现的方法 displayData() 声明了一个抽象类 Employess。然后我们在派生类中实现 displayData() 主体。这里需要注意的一点是,我们必须在派生类中使用 override 关键字作为抽象方法的前缀。

using System;
namespace oops
{
    //abstract class
    public abstract class Employess
    {
        //abstract method with no implementation
        public abstract void displayData();
    }

    //derived class
    public class test : Employess
    {
        //abstract class method implementation
        public override void displayData()
        {
            Console.WriteLine("Abstract class method");
        }
    }
    class abstractClass
    {
        static void Main(string[] args)
        {
            // class instance
            new test().displayData();
        }
    }
}

密封类

密封类与抽象类相反。虽然抽象类在派生类中被继承和细化,但密封类不能被继承。您可以创建密封类的实例。密封类用于防止通过继承进一步细化。

假设您是类库的开发人员,并且类库中的某些类是可扩展的,但其他类是不可扩展的,因此在这种情况下,这些类被标记为密封的。

using System;
namespace oops
{
    sealed class SealedClass
    {
        void myfunv();
    }

    public class test : SealedClass //wrong. will give compilation error
    {
    }
}

Interface

接口是一组必须在派生类中实现的相关函数。接口的成员是隐式公共和抽象的。接口类似于抽象类。首先,两种类型都必须被继承;其次,您不能创建任何一个的实例。虽然有以下几个不同之处;

  • Abstract 类可以包含一些实现,但接口不能。
  • 一个接口只能继承其他接口,而抽象类可以继承其他类和接口。
  • Abstract 类可以包含构造函数和析构函数,但接口不能。
  • Abstract 类包含字段,但接口不包含。

那么问题来了,该选择哪一个呢?选择接口是因为对于一个接口,派生类型仍然可以从另一个类型继承,并且接口比抽象类更直接。

using System;
namespace oops
{
    // interface
    public interface xyz
    {
       void methodA();
       void methodB();
    }

    // interface method implementation
    class test : xyz
    {
        public void methodA()
        {
            Console.WriteLine("methodA");
        }
        public void methodB()
        {
            Console.WriteLine("methodB");
        }
    }
    class interfaceDemo
    {
        static void Main(string[] args)
        {
            test obj = new test();
            obj.methodA();
            obj.methodB();
        }
    }
}

一个接口可以从其他接口继承,如下所示:

public interface xyz
{
    void methodA();
    void methodB();
}

public interface abc : xyz
{
    void methodC();
}

多态性

多态性是以相同的方式处理各种对象的能力。这是继承的重要好处之一。我们可以根据基引用的派生类型在运行时决定正确的调用。这称为后期绑定。

在下面的示例中,我们可以编写一个使用基本类型函数的泛型算法,而不是为 hrDepart、itDepart 和 FinanceDepart 类设置单独的例程。在基础抽象类中声明的方法 LeaderName() 根据我们在 2 个不同类中的需要重新定义。

using System;
namespace oops
{
    public abstract class Employee
    {
        public virtual void LeaderName()
        {
        }
    }

    public class hrDepart : Employee
    {
        public override void LeaderName()
        {
            Console.WriteLine("Mr. jone");
        }
    }
    public class itDepart : Employee
    {
        public override void LeaderName()
        {
            Console.WriteLine("Mr. Tom");
        }
    }

    public class financeDepart : Employee
    {
        public override void LeaderName()
        {
            Console.WriteLine("Mr. Linus");
        }
    }

    class PolymorphismDemo
    {
        static void Main(string[] args)
        {
            hrDepart obj1 = new hrDepart();
            itDepart obj2 = new itDepart();
            financeDepart obj3 = new financeDepart();

            obj1.LeaderName();
            obj2.LeaderName();
            obj3.LeaderName();

            Console.ReadKey();
        }
    }
}

慕源网 » C# .NET 的面向对象编程

常见问题FAQ

程序仅供学习研究,请勿用于非法用途,不得违反国家法律,否则后果自负,一切法律责任与本站无关。
请仔细阅读以上条款再购买,拍下即代表同意条款并遵守约定,谢谢大家支持理解!

发表评论

开通VIP 享更多特权,建议使用QQ登录