我是靠谱客的博主 干净羽毛,最近开发中收集的这篇文章主要介绍C#面向对象进阶:十二大修饰符一网打尽,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

    • 访问修饰
    • 类修饰符
    • 接口
    • virtual
    • 其他修饰符

前文提要:
????零基础掌握C#开发中最重要的概念 ????面向对象快速上手
????懂了委托,才算真正入门 ????学会泛型,迈向高手之路
????Winform,最友好的GUI ????Winform+OpenGL+MathNet处理Gauss光斑

在快速入门教程中已经疏解了一些面向对象的基本概念,但并不深入,只对类的构造和继承做了简单的介绍。下面将深入理解C#的面向对象,文集中针对12个修饰符以及接口,从内到外对类进行展开讲解。

访问修饰private, public, protected, internal
特殊类static, sealed, abstract; 接口interface
成员修饰static, sealed, abstract, overade, virtual
其他const, readonly, partial

由于面向对象本来就大肠包小肠,所以本节所有示例均不使用顶级语句。

访问修饰

在声明类成员和方法的时候,往往在类型前面加一个public或者private,比如public int add这种,用以标识外部是否可以访问,此即访问修饰符。

C#一共有6种访问修饰符,这些修饰符当然在类内都是可以访问的,但是在类外,根据是否为派生类、是否在相同程序集,共分为四种特殊情况。

  • publicprivate是两种极端情况,前者是公共属性,任何时候都可以访问;后者是私有属性,除了类内,其他人都不可以访问。
  • protected为保护类型,只有派生类可以访问;internal为内联类型,只有相同的程序集可以访问。
  • private protectedprotected的基础上缩减一步,只允许相同程序集的派生类访问。
  • protected internalprotected的基础上拓展一步,还允许不同程序集的派生类访问。

下面新建一个项目oopTest,在命名空间oopTest中新建一个TestClass,分别用这六种不同的访问修饰符做一个函数。

public class TestClass
{
    public void pubPrint(){
        Console.WriteLine("public");
    }
    private void priPrint(){
        Console.WriteLine("private");
    }
    protected void proPrint(){
        Console.WriteLine("protected");
    }
    internal void IntPrint(){
        Console.WriteLine("internal");
    }
    private protected void priProPrint(){
        Console.WriteLine("private protected");
    }
    protected internal void proIntPrint(){
        Console.WriteLine("protected internal");
    }
}

下面在不同的应用场景调用这六个函数,首先是main函数中调用,结果如下,其中private, protected, private protected标红了。

在这里插入图片描述

然后新建一个子类,在子类中调用这六个函数,效果如下

在这里插入图片描述

接下来右键解决方案,新建一个项目LibTest,将TestClass移动到这个项目的命名空间下。然后右键oopTest的依赖项,在引用管理器中勾选LibTest,然后在oopTestusing LibTest,这样名义上就可以调用LibTest库中的内容了。

这次,Main函数和子类的表现如下,不考虑public,在子类中,protected, protected internal还依旧坚挺,而在Main函数中则全员阵亡,此时在Main函数中调用TestClass,相当于既不在相同程序集,也不是派生类。

在这里插入图片描述

类修饰符

C#除了质朴的class,还有三种被修饰的类,分别是静态类、密封类以及抽象类,三者分别用修饰符static, sealed, abstract来修饰,这三者互不兼容,说明如下

  • static 不能被实例化为对象,且只能从基类派生
  • sealed 密封类不能被继承
  • abstract 抽象类只能被继承,不能被实例化

对于习惯了面向过程或者函数式编程的人来说,使用静态类有种找到家的感觉。静态类不能实例化的同时,支持不开箱即用,静态类就像是一个函数库,可直接调用,像下面这种。

static class StaticClass
{
    public static void printStatic()
    {
        Console.WriteLine("static class");
    }
}
class Program
{
    static void Main(string[] args)
    {
        StaticClass.printStatic();  
    }
}

密封类和抽象类互为相反,前者不能被继承,后者只能被继承。密封类禁止派生,除了能够带来一些安全性上的优势之外,有时也能略微提高一点成员调用速度。

抽象类用于提供一个基类定义,并让多个派生类共享。抽象类中的方法,既可以是抽象的,也可以不是抽象的,通过abstract修饰的方法,只能声明,不能实现其具体内容。

public abstract class AbstractClass
{
    // 用abstract修饰之后,不可实现
    public abstract void printSubAbstract();
    // 不用abstract修饰,必须写细节
    public void printAbstract()
    {
        Console.WriteLine("Abstract");
    }
}

如果一个普通类或者密封类继承了抽象类,那么这个类必须实现抽象类中所有的抽象方法,其中需要用到overide;而抽象类继承抽象类则不必。

public class SubAbstract : AbstractClass
{
    public override void printSubAbstract()
    {
        Console.WriteLine("Abstract");
    }
}

// 如果没有abstract修饰,是会报错的
public abstract class AbstractAbstract : AbstractClass
{ }

接口

抽象类其实已经够抽象的了,但还有更加抽象的类,即接口interface。接口太特殊了,一般在命名的时候多以I开头。

public interface ITest
{
    public void printI();
}

public class Test : ITest
{
    public void printI()
    {
        Console.WriteLine("interface");
    }
}

抽象类和接口都是便于被继承的类,二者的区别大致为,抽象类大而全、接口小而专,关于二者的区别,网上说的很精辟了

飞机会飞,鸟会飞,二者都继承了“飞”这个接口;但是F22属于飞机抽象类,鸽子属于鸟抽象类。
另一方面,如果要去买飞机,你必须说明白要买什么飞机(派生类),如果光说我要买飞机,那没人知道你要买什么飞机(抽象类不能实例化)。

virtual

诸如abstract, static, sealed除了可以修饰类之外,也可以修饰类中的方法,而且其含义也是高度相似的:

  • 密封方法像密封类一样,无法被重写
  • 抽象方法只能在抽象类或接口中使用,这在前面已经举例说明了,而已经abstract修饰之后,想要重写就需要用到overide修饰符。
  • 静态方法也和静态类一样,可以在其所在类没有实例化的情况下,直接调用,非常便捷。

静态方法和实例方法在调用上存在差异,暗示着二者在编译期间就存在不同的行为。由于静态方法在类未经实例化的时候就已经可以调用了,所以程序一经编译,就已经分配好了静态方法的内存,这块领地既不可更改也不可销毁;而普通的实例方法则在对象实例化之后紧跟着被创建,对象被回收之后,也就紧跟着被销毁。

除了这几个老面孔之外,基类成员还有一个修饰符virtual,为了理解这个修饰符,先考虑这样一件事,假设B是A的子类,那么我先声明一个A的对象b,然后再将b实例化成B,那么对于被B重写的方法,应该执行哪一个?

说起来很绕,具体看代码如下

public class A
{
    public void print()
    {
        Console.WriteLine("A");
    }
}
public class B : A 
{
    public void print()
    {
        Console.WriteLine("B");
    }
}
class Program
{
    static void Main(string[] args)
    {
        A b;
        b = new B();
        b.print();
    }
}

换言之,A b指明了bA类;但b = new B()却建了个B类,那么b.print()执行谁呢?

结果有些出乎意料,最后输出了B,看来对于方法重写来说,声明比实例化更重要。

但这里有一个吊诡之处,如果声明一个抽象类,然后对抽象类实例化为其子类,再调用子类中实现的抽象方法,最终会执行谁?

这个问题很好回答,抽象方法没实现,必须执行子类的方法,所以这里看似出现了一个矛盾。

但仔细思量却可以发现,被abstract修饰的方法,在子类中是不能直接重写的,而必须用到overide关键字,换言之,方法经overide修饰后,会导致实现声明更重要。

但接下来又有一个问题,abstract不能在常规的类中使用,而且overide不能重载普通方法。就在这个逻辑崩溃的时刻,虚方法virtual出现了。通过virtual修饰的方法,可以被overide重写。

public class A{
    public virtual void print(){
        Console.WriteLine("A");
    }
}
public class B : A {
    public void print(){
        Console.WriteLine("B");
    }
}
public  class C : A{
    public override void print(){
        Console.WriteLine("C");
    }
}
class Program{
    static void Main(string[] args){
        A b;
        A c;
        b = new B();
        b.print();      // 输出 A
        c = new C();
        c.print();      // 输出 C
    }
}

其他修饰符

一般新建一个WPF程序,VS默认的入口类都这么写

public partial class MainWindow : Window

其中partial并没有定义一种新的类,相比之下,更像是个语法糖,允许程序员在两个文件中写同一个类。作为窗口程序,其MainWindow往往特别大,显得partial非常常见。

const表示声明一个常量;readonly表示声明一个只读变量,也叫动态常量。前者在编译时直接被替换为对应常量的值,所以不费内存;后者的值则在运行之后获得,但不可被更改。

const在使用时有一定的限制,即只能修饰基元类型,比如int, string之类的,如果有一个List<int>,则只能通过readonly修饰。一般如果想把一个泛型结构当作永不更改的常量使用,比较习惯的用法是static readonly

C#中的修饰符当然不止这些,但extern用于调用外部程序,尤以调用dll时居多;in, out, new主要应用在泛型方面,volatile, async则与并发编程更为密切,这些内容都不是三言两语说得清的,所以就不放在对OOP的介绍中了。

最后

以上就是干净羽毛为你收集整理的C#面向对象进阶:十二大修饰符一网打尽的全部内容,希望文章能够帮你解决C#面向对象进阶:十二大修饰符一网打尽所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(54)

评论列表共有 0 条评论

立即
投稿
返回
顶部