.NET Core CSharp初级篇 1-3

本节内容为面向对象初级教程

简介

面向对象是整个C#中最核心最有特色的一个模块了,它很好的诠释了程序与现实世界的联系。

面向对象的三大特征:继承、多态、封装;继承的含义可以理解为集合中的包含关系,例如人类属于动物类的一个分支,这就是一种继承。多态的理解就可以是人的呼吸用肺,鲤鱼使用鳃,这就是一种同种操作对应不同的实现。封装可以理解为一堆零件可以组成一个手机,这个过程就叫做封装。而将电脑显卡等拆下来组装成另一台电脑,则属于类的拆箱装箱。

封装一个类的好处在哪里呢?我举一个例子:

首先,我们考察一个常见的生活实例来进行说明,例如每当发工资的日子小王都来到 ATM 机

前,用工资卡取走一笔钱为女朋友买礼物,从这个很帅的动作,可以得出以下的结论:

  • 小王和 ATM 机之间,以银行卡进行交互。要取钱,请交卡。
  • 小王并不知道 ATM 机将钱放在什么地方,取款机如何计算钱款,又如何通过银行卡返回小王

    所要数目的钱。对小王来说,ATM 就是一个黑匣子,只能等着取钱;而对银行来说,ATM 机就

    像银行自己的一份子,是安全、可靠、健壮的员工。
  • 小王要想取到自己的钱,必须遵守 ATM 机的对外约定。他的任何违反约定的行为都被视为不

    轨,例如欲以砖头砸开取钱,用公交卡冒名取钱,盗卡取钱都将面临法律风险,所以小王只能

    安分守己地过着月光族的日子。

    那么小王和 ATM 机的故事,能给我们什么样的启示?对应上面的 3 条结论,我们的分析如下:
  • 小王以工资卡和 ATM 机交互信息,ATM 机的入卡口就是 ATM 机提供的对外接口,砖头是塞不

    进去的,公交卡放进去也没有用。
  • ATM 机在内部完成身份验证、余额查询、计算取款等各项服务,具体的操作对用户小王是不

    可见的,对银行来说这种封闭的操作带来了安全性和可靠性保障。
  • 小王和 ATM 机之间遵守了银行规定、国家法律这样的协约。这些协约和法律,就挂在 ATM 机旁边的墙上。

具体来说,封装隐藏了类内部的具体实现细节,对外则

提供统一访问接口,来操作内部数据成员。这样实现的好处是实现了 UI 分离,程序员不需要知道

类内部的具体实现,只需按照接口协议进行控制即可。同时对类内部来说,封装保证了类内部成

员的安全性和可靠性。在上例中,ATM 机可以看做封装了各种取款操作的类,取款、验证的操作

对类 ATM 来说,都在内部完成。而 ATM 类还提供了与小王交互的统一接口,并以文档形式——

法律法规,规定了接口的规范与协定来保证服务的正常运行

类属于在堆分布的变量,意味着它的大小是不固定的。可以动态的进行调节。

创建与实例化类

类的创建非常的简单,实例化也非常的简单,创建类就是把一个具体的事物抽象化,实例化就是将抽象化的类给转换成具象化的对象。例如我们定义一个People类,内含若干个变量;

//定义类使用class关键字
class People
{
public string Name;
public int Age;
}
//实例化类
People p = new People();

或许你还看不太懂这些,别急,请继续往下看。

修饰符

访问控制修饰符

  • public:对访问没有任何限制,属于最高级别的访问权限
  • private:私有权限,最低级别的访问,只能在声明的代码段(类)中使用。
  • protected:保护权限,只有继承了该类才可以使用。
  • internal:仅包含当前程序集使用
  • protect internal:同一个程序集的类和其派生的类可以使用

这样说或许过于抽象,我们这样来解释吧,一个程序就类似一个公司,public就好比是董事长、CEO一类的权限,拥有着最高级别的访问;protected你可以理解为部门经理,它的下属就是继承该部门,下属可以访问父类(部门)的资源,但不可以访问其他部门的protected资源,体现为一种纵向的权限控制。Internal类似与考勤部门,无论该部门是否属于考勤部门领导,考勤部门都可以管辖其他部门,体现为一种横向的权限控制。Protected internal则是具有两种属性。

可选修饰符

  • static(可用于类内成员):静态的,表示只被创建一次,属于所有对象公用的变量
  • sealed:密封类,禁止类被继承
  • abstract:抽象类,要求类被继承,并且不能实例化
  • virtual(不能用于类):表示可以被重写
  • readonly(用于字段):表示该字段只读
  • const(用于字段):表示常量
  • extern(用于函数):表示该函数由外部实现
  • async(用于函数):表示该函数为异步函数

函数

函数也被称为方法,是包含一系列语句的代码块。封装了类的行为,提供了类的对外表现。用于将封装的内部细节以公有方法提供对外接口,从而实现与外部的交互与响应。例如,从上面属性的分析我们可知,实际上对属

性的读写就是通过方法来实现的。因此,对外交互的方法,通常实现为 public。程序通过调用该方法并指定任何所需的方法参数使语句得以执行。 在 C# 中,每个执行的指令均在方法的上下文中执行。

函数的构成由访问控制关键字+修饰符+返回值+函数名称+函数参数+函数体,如果一个类内函数或者其他成员使用了static关键字,则可以不实例化类对其进行调用,因为使用了static标明的成员,属于全体该类对象共有。例如下面这个例子:

class Man
{
static void GettingOld()
{
//life - 1s
}
public void Eat()
{
}
}
//静态方法可以直接调用
Man.GettingOld();
Man man = new Man();
man.Eat();

函数的使用如下,其中x,y称为形参,x1,y1称为实参,通常对形参的操作并不会影响到实参

public static int Add(int x,int y)
{
x++;y++;
return x+y;
}
int x1 =5;
int y1 =6;
Add(x1,y1);
//带有默认参数
public static int Add(int x,int y=4)
{
}
//不定参数
public void Add(params object[] a)
{
}

当然不是所有的方法都被实现为 public,否则类内部的实现岂不是全部暴露在外。必须对对外

的行为与内部操作行为加以区分。因此,通常将在内部的操作全部以 private 方式来实现,而将需要与外部交互的方法实现为 public,这样既保证了对内部数据的隐藏与保护,又实现了类的对外交互。例如在 ATM 类中,对钱的计算、用户验证这些方法涉及银行的关键数据与安全数据的保护问题,必须以 private 方法来实现,以隐藏对用户不透明的操作,而只提供返回钱款这一 public 方法接口即可。在封装原则中,有效地保护内部数据和有效地暴露外部行为一样关键。

函数的重载与重写

重载函数表示使用同一个函数名,通过参数的不同,从而实现使用同一个名称可以选择调用多种函数。例如实现两个数字的相加,传入的有可能是整型参数也有可能是浮点型参数,因此,我们选择使用重载函数去实现。以下是一个重载的例子;

public static int Add(int x,int y)
{
return x+y;
}
public static double Add(double x,double y)
{
return x+y;
}
Add(1,2);//调用第一个Add
Add(1.1,2.2);//调用第二个函数

通过调用函数时传入的参数不同,就可以很简单的用不同方法实现。

函数重写则多出现在面向对象的多态性中,这里我不会很详细的讲解,在后面会有一个详细的讲解。重写就可以理解为,人呼吸用肺,大部分鱼类呼吸用鳃,呼吸这个函数就是在两个类中被重写过(即实现方法在不同的类中)。具体的实现我会在后一步进行讲解

需要注意的是,重载需要在参数上有本质的区别,例如个数、类型不同,重写则需要方法可以被重写,使用override关键字表明重写的函数

类中重要的两个函数

构造函数:构造函数是一种特殊的函数,它的签名和类名一致,并且没有返回值。它可以接受任意个参数。当类被实例化的时候,对应的构造函数会被调用。可以说,对象是通过调用类的构造函数进行创建。如果不指定构造函数,C#会自动调用默认的无参构造函数。如果重载了构造函数,并且传入了指定参数,则会调用对应的构造函数。

析构函数:类似与构造函数,但是调用是在GC(垃圾回收器)回收类对象的时候自动调用,通常无需去重写。例如:

class A
{
//默认无参构造函数
public A()
{
}
public A(int a)
{
}
// 析构函数
~A()
{
}
}
A a = new A(1);//调用第二个构造函数

字段和属性

(此处补充IL代码)

字段:类中具体实现存储数据的变量,你可以理解为各个零件。通常而言,字段不会对外进行开发访问权限。例如:幼儿园读书的小朋友类,里面有一个Age(年龄)字段。假设一个人实例化,我们给年龄赋值上1000。这符合常理吗?显然是不符合的。那么我们就要使用属性进行控制输入的变量。

属性:属性不存储数据,通常定义为 public,表示类的对外成员。属性具有可读、可写属性,通过 get 和 set 访问器来实现其读写控制。但是如果你使用默认的属性实现方法,例如public string a {get;set;},C#会自动的为你隐式生成一个私有的字段a。属性本质上是作为外部访问字段的一个媒介、桥梁,也称之为接口。通常来说,我们会将字段定义为私有的,将属性定义为公有的,通过属性去返回和设置其中的值。在这里,我们涉及到了两个从未见过的关键字——get,set。

get访问器:get访问器本质上是一个返回值为属性类型的函数,你可以使用dnSpy进行反编译查看。你一般需要在get中返回你需要访问的变量。

set访问器:使用value关键字接受外界传来的参数并且赋值给你的字段,本质上也只是一个函数,当你对属性赋值的时候,就会调用他的set控制块内的代码

class A
{
private int a;
public int A
{
get{return a;}
set
{
if(value>5)
{
a=value;
}
}
}
//自动生成一个b字段
public int B{get;set;}
}

选看 函数参数修饰符

对C#了解的人都知道,实参到形参的传递时存在一个数据备份的过程,因此我们对形参的操作本质上是对备份的变量进行操作,并不会影响到实参的值。但是在C#中,可以对形参进行修饰,使传入的变量按内存地址进行传入,这样就可以实现改变实参的值了。

ref 关键字

ref关键字的要求有以下几点:

  • 被调用函数的参数和调用时都必须使用ref标记参数
  • 传递到 ref 参数的参数必须初始化,否则程序会报错

例如:

        static void Main(string[] args)
{
int a = 10;
int b = 20;
Test(ref a,ref b);
Console.WriteLine("a:{0},b:{1}", a, b);
}
static void Test(ref int a, ref int b)
{
a = a+b;
b = 6;
}

out 关键字

out关键字的要求有以下几点:

  • 方法定义和调用方法都必须显式使用 out关键字

  • out关键字无法将参数值传递到out参数所在的方法中,只能传递参数的引用,所以out参数的参数值初始化必须在其方法内进行,否则程序会报错

        static void Main(string[] args)
{
int a=100;
int b;
Test(out a, out b);
Console.WriteLine("a:{0},b:{1}", a, b);
}
static void Test(out int a, out int b)
{
a = 1+2;
b = 1;
}

in关键字

in关键字的要求有:

  • 它让形参成为实参的别名,这必须是变量。 换而言之,对形参执行的任何操作都是对实参执行的。
  • in 参数无法通过调用的方法进行修改。 out 参数必须由调用的方法进行修改,这些修改在调用上下文中是可观察的,而 ref 参数是可以修改的
int readonlyArgument = 44;
InArgExample(readonlyArgument);
Console.WriteLine(readonlyArgument); // value is still 44 void InArgExample(in int number)
{
// Uncomment the following line to see error CS8331
//number = 19;
}

练习题

  • 请试着创建一个圆类(Circular),要求封装圆周率和半径(或直径),并且定义一个含有一个参数的构造函数,传入的是半径(直径)。并写入计算周长和面积的函数。

  • 定义一个用户类,要求用户名和密码不可以被访问,只允许设置,并且密码小于6位需要输出相应提示并且不进行赋值要求重新赋值。

前往Github获取更多本节资料(PPT,实例代码)

如果我的教程帮到了您,希望您动动小手,在GitHub给我一个star

Github

BiliBili主页

WarrenRyan's Blog

博客园

Reference

《你必须知到的.NET》

NET Core CSharp初级篇 1-3面向对象的更多相关文章

  1. .NET Core CSharp初级篇 1-1

    .NET Core CSharp初级篇 1-1 本节内容是对于C#基础类型的存储方式以及C#基础类型的理论介绍 基础数据类型介绍 例如以下这句话:"张三是一名程序员,今年15岁重50.3kg ...

  2. .NET Core CSharp初级篇 1-5 接口、枚举、抽象

    .NET Core CSharp初级篇 1-5 本节内容类的接口.枚举.抽象 简介 问题 如果你需要表示星期或者是某些状态,使用字符串或者数字是否不直观? 你是否发现,无论何种电脑,它的USB口的设计 ...

  3. .NET Core CSharp初级篇 1-6 类的多态与继承

    .NET Core CSharp初级篇 1-6 本节内容为类的多态与继承 简介 终于讲到了面向对象三大特性中的两大特性--继承与多态.通过继承与多态,我们能很好的将类的拓展性发挥到了极致.在下面的内容 ...

  4. .NET Core CSharp初级篇 类的生命历程

    .NET Core CSharp初级篇 1-7 本节内容为类的生命周期 引言 对象究竟是一个什么东西?对于许多初学者而言,对象都是一个非常抽象的知识点.如果非要用一句话描述,我觉得"万物皆对 ...

  5. .NET Core CSharp初级篇 1-8泛型、逆变与协变

    .NET Core CSharp初级篇 1-8 本节内容为泛型 为什么需要泛型 泛型是一个非常有趣的东西,他的出现对于减少代码复用率有了很大的帮助.比如说遇到两个模块的功能非常相似,只是一个是处理in ...

  6. .NET Core CSharp初级篇 1-2 循环与判断

    .NET Core CSharp初级篇 1-2 本节内容循环与判断 循环 循环是一个在任何语言都是极为重要的语法,它可以用于很多东西,例如迭代数组等等.在C#中,语法层面的循环有:for , fore ...

  7. CSharp初级篇 1-4 this、索引器、静态、常量以及只读

    .NET Core CSharp初级篇 1-4 本节内容为this.索引器.静态.常量以及只读 简介 在之前的课程中,我们谈论过了静态函数和字段的一小部分知识,本节内容中,我们将详细的讲解关于对象操作 ...

  8. .NET Core CSharp 中级篇 2-2 List,ArrayList和Dictionary

    .NET Core CSharp 中级篇 2-2 本节内容为List,ArrayList,和Dictionary 简介 在此前的文章中我们学习了数组的使用,但是数组有一个很大的问题就是存储空间不足,我 ...

  9. .NET Core CSharp 中级篇 2-1 装箱与拆箱

    .NET Core CSharp 中级篇 2-1 本节内容为装箱与拆箱 简介 装箱和拆箱是一个相对抽象的概念.你可以想象一下一堆满载货物的大卡车,他是由许多工人将货物集中堆放装入的,对于我们而言在没有 ...

随机推荐

  1. VS2012 调试Web项目 遭遇 HTTP 错误 500.23 - Internal Server Error

    原文:VS2012 调试Web项目 遭遇 HTTP 错误 500.23 - Internal Server Error 在使用vs2012 调试Web站点时 报错 500.23,详细如图 此错误是因为 ...

  2. linux的自动化操作相关使用方法汇总 专题

    Crontab中的除号(slash)到底怎么用? crontab 是Linux中配置定时任务的工具,在各种配置中,我们经常会看到除号(Slash)的使用,那么这个除号到底标示什么意思,使用中有哪些需要 ...

  3. SGI地址模式: O32, N32和N64

    背景 MIPS R10000芯片支持MIPS ABI.遵循这一标准的程序能够运行在遵循这一标准的任何处理器/系统上.目前,主要的支持者有SGI,西门子,Nixdof, Tandem, Pyramid, ...

  4. 为何只能在其关联的线程内启动timer?(Qt会检查一致性,否则就不执行)

    为何只能在其关联的线程内启动timer? 在QTimer源码分析(以Windows下实现为例) 一文中,我们谈到: QTimer的是通过QObject的timerEvent()实现的,开启和关闭定时器 ...

  5. UITableViewStyleGrouped 设置表头出现section不为0的问题

    UITableViewStyleGrouped 设置表头出现section不为0的问题 1.如果使用UITableViewStylePlain样式的表格,那么header是会在表格滑动的时候在顶部悬浮 ...

  6. 以太网,IP,TCP,UDP数据包分析(此文言简意赅,一遍看不懂的话,耐心的看个10遍就懂了,感谢作者无私奉献)

    1.ISO开放系统有以下几层: 7 应用层 6 表示层 5 会话层 4 传输层 3 网络层 2 数据链路层 1 物理层 2.TCP/IP 网络协议栈分为应用层(Application).传输层(Tra ...

  7. 深入理解Java G1垃圾收集器

    本文首先简单介绍了垃圾收集的常见方式,然后再分析了G1收集器的收集原理,相比其他垃圾收集器的优势,最后给出了一些调优实践. 一,什么是垃圾回收 首先,在了解G1之前,我们需要清楚的知道,垃圾回收是什么 ...

  8. 代码审计之seacms v6.45 前台Getshell 复现分析

    1.环境: php5.5.38+apache+seacms v6.45 seacms目录结构: │─admin //后台管理目录 │ │─coplugins //已停用目录 │ │─ebak //帝国 ...

  9. 03- 基本的SQL语句介绍

    01 库的操作新增库create database db1 charset utf8; # 由于在my.ini中已经配置了字符集,所以,charset utf8可以不写 查库# 查看当前创建的数据库s ...

  10. ElasticSearch学习(一):ElasticSearch介绍

    一.ElasticSearch是什么? ElasticSearch是一款非常强大的.基于Lucene的开源搜索及分析引擎,可以帮助你从海量数据中,快速找到相关的数据信息. 比如,当你在GitHub上搜 ...