本文介绍
作者在校期间做班教课近两年来的一些精华内容,既然现在已经毕业了,把以前做成教程的内容拿出来分享给大家,本文章也适用于初识C#使用想要学习C#的同学,本文章课程内容均是作者个人观点意见看法,希望大家喜欢,支持的可以打个赏哈哈。
文章中几句内容,几千字之内说完整个C#语言,未免有些唐突,一定会一些地方处理的不是得特别好,希望大家谅解!
C#程序介绍
我们先来看一下C#程序的构造
using System;
class program
{
static void Main(string[] args)
{
Console.WriteLine(“Hello World");
}
}
-
using System
C#命名空间 -
class program
C#程序主类名字 -
static void Main(String[] args)
C#程序主方法 应用程序入口 -
Console.WriteLine
C#程序输出函数 输出到控制台上
好啦,这样我们就完整的写好了一个简单C#程序,这样就是C#程序的构造,接下来我们来看看C#程序的常用的变量
C#变量和常量
- 数字类型
我们只需要记住int和double类型就可以,int代表整数,double代表小数。
private int i=0; //定义一个整数变量并赋值为0
private double i
- 布尔类型
布尔类型顾名思义,布尔类型只包含两种值那就是对或错,true和false
private bool b1;
private bool b2;
- 字符串类型
也是一个比较常用的类型,用于存储一个字符串。
private string s1;
好了知道这些我们就可以进入C#程序设计了,但是在学习整体代码之前我们还需要了解一些东西*
来到C#世界前的踏板
控制台输入输出
- 控制台的输入
console.Read(); //读取一个字符
console.ReadLine(); //读取一行字符
- 控制台的输出
console.WriteLine(); //将指定数据输出到控制台
自增自减运算
-
自增运算(++)
前置自增运算符 :前置自增运算符先对变量减1,然后使用变量;
后置自增运算符 :后置自增运算符先使用变量,然后对变量减1 -
自减运算(--)
前置自减运算符 :前置自减运算符先对变量减1,然后使用变量;
后置自减运算符 :后置自减运算符先使用变量,然后对变量减1
** 好了该介绍的介绍完了,我们可以来到C#的小世界,来探索C#的根源了 哈哈哈**
C#程序控制语句
IF语句
if(条件)
{
//如果条件成立,则执行这里
} else
{
//如果条件不成立,则执行这里
}
我们在学习C#程序时候这几个程序控制语句需要知道,比如看这个if语句,顾名思义就是去判断我们的给出的条件是否成立,成立执行什么不成立执行什么。
FOR循环
for(表达式1;表达式2;表达式3)
{
语句块;
}
for循环用于我们需要执行循环的操作时候使用,比如我们循环输出1到100中能被7整除的数
int i;
for(i=1;i<100;i++){
if(i%7 == 0) Console.WriteLiine(i);
}
- 这里我们使用循环操作用于循环出1到100的100个数,然后一一去和7相除看看是否能被整除如果整除我们就输出这个数。
- 这里表达式1时循环体中的初始值,表达式2时循环的终止条件,表达式3时循环体自增的步伐,这里我们自增的步伐是1,第一次循环i是1,第二循环则变成了2。
好了我们基本上介绍完了基础要领,我们来看几道考试必考的题目,来活动活动
!!!这里我们先要说道一个重点!!就是我们做编码,一定不要去背代码,一定要学会去理解代码,然后把我们需要解题的过程分步记录下来,然后我们按照步骤去编写代码!!!
习题1 (水仙花数)
输出100~999之间所有的水仙花数,水仙花数各位数字的立方和等于这个三位数本身。例如:371就是一个水仙花数,371 = 33 + 73 + 13。
-
这里先说什么是水仙花数呢?水仙花数就百位上的数字的立方加上十位上的数的立方再加上个位上的数的立方和为这个数本身,这样的数就是水仙花数。
-
这里我们先说说水仙花数的过程吧
由题目可知我们要求100到999内的水仙花数,所以呢我们要做一个100到999的循环,然后判断每一个数是不是水仙花数。
第一步 写出100到999的循环
第二步 取出百位十位个位上三个数
第三步 判断这个数是不是水仙花数,并输出
这里我们给出代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/**
* 水仙花数
*/
namespace Demo5
{
class 水仙花数
{
static void main(string[] args)
{
for (int i = 100; i <= 999; i++) {
int a = i % 10;
int b = (i % 100) / 10;
int c = i / 100;
if (a*a*a + b*b*b + c*c*c == i){
Console.WriteLine(i);
}
}
Console.ReadKey();
}
}
}
- 这里我们说明一下,十位上的数是怎么取出来的,哈哈,这里我们先对这个百位数除10取出商值,假若这里百位数是123,除以10之后的商为12,然后再对商值除10进行取余便得出商值2,到这里便取出了十位数2!哈哈,明白了嘛!
习题2(回文数)
编写一个程序来判断输入的五位数是不是回文数?
-
我们先来说明一下什么是回文数?例如12321 45654 这样的数 以中间数为分割 左右对称的数就是回文数。我们的任务就是去判断这个数是不是回文数
-
我们这里分出两个方法来解释这个回文数。第一种比较简单学校的要求,去判断一个5位数是不是回文数,我们去掌握这个就可以啦,另一钟方法我们用字符串去判断是不是回文数,这种方法可以判断5位以上的数是不是回文数。
这里我们先列出代码
//这里我们只写主函数里的代码啦
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/**
* 回文数
*/
namespace Demo5
{
class 回文数
{
static void main(string[] args) {
int n;
n = int.Parse(Console.ReadLine());
int a, b, c, d;
a = n / 10000; 万位数
b = (n / 1000) % 10; 千位数
c = n / 10 % 10; 十位数
d = n % 10;个位数
if((a==d)&&(b==c)){
Console.WriteLine("是");
}else{
Console.WriteLine("不是");
}
}
}
}
这里的步骤也比较简单,分别取出万位数、千位数、十位数、个位数,然后判断万位数和个位数是否相同,十位数和千位数是否相相同,如果都相同则说明这是个回文数
接下来我们来给出下一种方法
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string a;
a=Console.ReadLine();
int len = a.Length-1;
int x,y;
for (x = 0,y = len - x; x <= (len+1)/ 2; ++x,--y)
{
if (a[x]!=a[y])
{
Console.WriteLine("不是回文");
break;
}
}
if(x>=y)
{
Console.WriteLine("是回文");
}
Console.ReadKey();
}
}
}
这里是循环去判断一一对应的位置上的数字是否相同,如果有一个不同则跳出循环,说明它不是个回文数,如果一一相同则说明它是个回文数。这里我们就不做步骤讲解了,如果有兴趣的同学可以评论或者加我,我再给你们一一详解。
习题3(循环的应用实例)
编写一个C#控制台应用程序,依次输出100~200之间的素数(素数是这样的整数,它除了能表示为它自己和1的乘积以外,不能表示为任何其他两个整数的乘积),要求每5个素数换行显示?
- 同理,我们先说明下什么是素数?素数就是一个正整数,除了被1和素数本身整除的数就是素数
同理,我们这里一样先说明下使用编程求出100到200中素数的过程
第一步 写出100到200的循环 做出100到200的循环用于判断100到200中每一个数是不是素数
第二步 判断循环中的这个数是不是素数
第三步 输出素数 如果这个数是第五个数 输出换行符
我们这里给出代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Demo5
{
class 循环素数
{
static void main(string[] args) {
int i, j, n = 0;
for (i = 100; i <= 200; i++) {
for (j = 2; j < i; j++)
{
//从2开始到n-1用循环中的数与之相除,如果整除这个数变不是素数
if (i % j == 0) break;
if (j == i - 1)
{
Console.Write(i + " ");
n++;
}
}
if (n % 5 == 0) Console.WriteLine(); //如果是第5个数换行
}
Console.ReadKey();
}
}
}
数组
数组顾名思义,就是存了一组数据的数据结构叫做数组,如下数组的定义
//静态数组的定义
int [] arr={1,2,3,4,5};
int []array={25,14,13,8,7}
//动态数组的定义
int[] arr = new int[5];
动态数组的定义,int[] arr = new int[5];,arr是数组的名字。5是动态数组的长度。
习题3(数组的应用实例)
包含十个元素的整型数组:int[] arr = { 32, 87, 3, 589, 12, 4076, 2000, 8, 622, 127 }。请使用冒泡排序,将它们按照从大到小的顺序重新排列并输出。
- 冒泡排序的基本思想:以升序为例,含有n个元素的数组原则上要进行n-1趟排序。对于每一趟的排序,从第一个数开始,依次比较相邻两个数的大小。如果前一个数比后一个数大,则进行交换。这样一趟过后,最大的数将会出现称为最末位的数组元素。第二趟则去掉最后一个数,对前n-1个数再按照上面的步骤找出最大数,该数将称为倒数第二位的数组元素,......,n-1趟过后,就完成了排序
既然是这样,我们来说说冒牌排序的编程过程思路
第一步 定义数组
第二步 做一次循环 从头到尾
第三步 做嵌套循环 从外层循环的头到外层循环的尾 依次比较大小 将大的数交换
第四步 输出数组
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
int[] arr = { 32, 87, 3, 589, 12, 4076, 2000, 8, 622, 127 };
int temp;
for (int i = 0; i < arr.Length - 1; i++)
{
for (int j = 0; j < arr.Length-i-1; j++)
{
if (arr[j] < arr[j + 1])
{
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
foreach (int a in arr) //数组中的循环 可以使用for循环,使用i值来读出数组中数据,也可以使用foreach循环数组
{
Console.Write(a + " ");
}
}
}
}
习题4(杨辉三角)
输出杨辉三角
杨辉三角的属性特点如下
每个数等于它上方两数之和。
每行数字左右对称,由1开始逐渐变大。
第n行的数字有n项。
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
哈哈,看起来是不是特别的复杂,其实很简单的,这里一共是5行,第一行1个数,第二行2个数,第三行3个数,一次往下每一个数,是上一行对应两个2之和,再加上每一行前加上n-i个空格,依次类推整体输出后就是杨辉三角!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/**
* 杨辉三角
*/
namespace Demo5
{
class 杨辉三角
{
static void main(string[] args) {
Console.WriteLine("杨辉三角");
int n = int.Parse(Console.ReadLine());
int[][] a = new int[n][];
for (int i = 0; i < a.Length; i++) {
a[i] = new int[i + 2];
a[i][0] = 1;
a[i][i + 1] = 0;
for (int j = 1; j < a[i].Length - 1; j++) {
a[i][j] = a[i - 1][j - 1] + a[i - 1][j];
}
}
for (int i = 0; i < a.Length; i++) {
for (int j = 0; j < a.Length-i; j++) {
Console.Write(" ");
}
for (int j = 0; j < a[i].Length; j++) {
if (a[i][j] != 0)
Console.Write(a[i][j]+" ");
}
Console.WriteLine();
}
Console.ReadKey();
}
}
}
函数
函数是什么呢?顾名思义,早在我们初中数学就学过函数,例如f(x)=x+x^2,这里如果我们代入x为2,这个函数是不是会给我们一个值值为6,c#中的函数和我们初中学的函数概念是一样的,在这里我们管这个值叫做返回值,x则是参数。
下面我们通过一段代码来熟悉下函数的定义构造和用法
注意:主类中写自定义函数,需要加上static
习题5 函数求和
class Program
{
static int add(int x, int y)
{
int sum;
sum = x + y;
return sum; //函数的返回值,返回谁?在这里定义
}
static void Main(string[] args)
{
int ret;
ret = add(3,5);
Console.WriteLine(ret);
}
}
- 这里我们定义了一个叫做add的函数
int add(int x ,int y);
这里的返回值是int
类型,参数是两个参数int x
和int y
。 - 当我们在主函数里调用这个子函数并传入2个参数分别分3和5,然后执行到子函数中,子函数中执行两个数相加并返回,获得8,在主函数中我们使用ret去获取这个值,最后我们输出这个数。这样就是子函数啦。
递归
- 相信大家都应该知道或者听说过递归,想必大家看了书本、课件上的递归的介绍和编码一定会感觉非常困惑,怎么会这么复杂?其实递归非常的简单。
- 我们从一道递归例题(斐波那契数列)说起,来理解递归的原理。斐波那契数列(1 1 2 3 5 8 13 21)数列上每一个数都是前两个数的和,这样我们就可以总结出一个公式!
- 当n=1,n=2时候数列上的值为1
当n>2数列上的值为(n-2)+(n-1)两个数的和 - 这样我们可以写出递归函数代码
if(n==1) return 1;
if(n==2) return 1;
if(n>2) return f(n-1)+f(n-2);
我们需要定义一个递归函数,名字为f,参数为n,返回值类型为int类型,这样我们给出全部代码
namespace Fibonacci_Sequence
{
class Program
{
static int f(int n) //自定义递归函数
{
if (n == 1) return 1;
if (n == 2) return 1;
if (n > 2 ) return f(n-1) + f(n-2);
}
static void Main(string[] args)
{
int n= 10;
int fib = f(n);
Console.WriteLine(fib);
}
}
- 我们可以根据这个递归公式列出所有取出的值
f(1) = 1
f(2) = 1
f(3) = f(2) + f(1) = 2
f(4) = f(3) + f(2) = 3
...
附加练习 阶乘递归
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/**
* 函数阶乘
*/
namespace Demo5
{
class 函数阶乘
{
static int f(int n) {
int ret = 0;
if (n < 0) Console.WriteLine("请输入一个非负整数");
if (n == 0 || n == 1)
ret = 1;
else ret = n * f(n - 1);
return ret;
}
static void main(string[] args) {
int n = int.Parse(Console.ReadLine());
int result = f(n);
Console.WriteLine(result);
Console.ReadKey();
}
}
}
这样看起来是不是简单一些,其他的递归也类似,先写出递归方程,然后做成递归函数编码,然后再在主函数中调用,这样递归就可以实现了!
面向对象编程(类)
- 之前我们学习到的所有内容是不是和C语言有些类似?但是我们为什么要学习C#呢,因为他比C语言要更灵活一些,一些处理更加方便一些,比如这章我们说到的面向对象编程。
- 面向对象编程,什么是面向对象编程?其实就是类和类的实例化,所什么又是类呢?在我们学习C语言的时候,假若我们求一个长方形的面积,我们只需要知道场长和宽就可以求出长方形的面积,但是我没每次要求长方形面积的时候,我们都需要重新写一下这个函数方程才能求出长方形的面积,但是我们有了面向对象编程之后(类)我们就不再需要每次都去重新写一个方程去求面积!
- **所以我们只需要将这个长方形的长和宽还有求长方形面积的公式封装起来,以后我们每次需要求长方形面积的时候我们只需要实例化这个封装类就可以使用这个长方形,并可以根据长和宽求出长方形面积了!
**
接下来让我们进入C#类的编程吧,在这里我们先要了解一些东西,关于面向对象编程的知识
- **属性读写(get set访问器) ** 当我们在类中定义了一些变量,在其他类中是无法直接访问的(public属性除外),这时候我们就需要索引器来帮助我们取出成员变量(类中定义变量)
- 构造函数 当我们实例化类的时,帮助我们初始化类中成员变量(下面会详细讲解什么实例化)
-
重载构造函数 构造函数可以有多个,当我们传入参数不同时,会执行不同的构造函数
接下来让我们通过一段代码来理解下什么是面向对象编程吧!
习题6 自定义类
自定义类长方形,类中包含长方形的属性长和宽,包含构造函数和属性读写(get set访问器),还包括长方形求面积函数
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Demo5
{
class RectAngle {
private int l;
private int w;
public RectAngle(int l, int w) {
this.l = l;
this.w = w;
}
public void Calc() {
int square = l * w;
Console.WriteLine(square);
}
}
class 自定义长方体类
{
static void main(string[] args)
{
RectAngle r = new RectAngle(int.Parse(Console.ReadLine()), int.Parse(Console.ReadLine()));
r.Calc();
}
}
}
- 我们先来说一下什么是实例化,当我们制作了一个长方形类,这个类代表的是所有的长方形,我们无法使用它来计算长方形面积因为我们没有长方形的长宽,我们需要把这个单独拿出来并赋值它长和宽,这样我们就可以根据长和宽求出长方形的面积了。
- 属性读写?WTF?其实属性读写不需要你去写,我们使用的VS可以自动帮我们生成,在我们定义了一个private成员变量之后,在vs中右键重构方法vs即可以帮助我们完成属性读写的get set方法
- 构造方法
索引器
索引器的声明类似于属性的声明,是由get和set访问器组成,索引器提供了对类中的数组元素的直接访问功能。即,如果一个类定义了索引器,这个类的实例就可以使用数据访问运算符“[]”对数组元素进行访问
其实索引器很简单,如下索引器的定义
string[] arr = new string[5];
public string this[int index]
{
get
{
return arr[index];
}
set
{
arr[index] = value;
}
}
- 我们需要在类中先声明一个数组,这样才能方便我们使用索引器来直接访问我们类中数组中的数据
string[] arr = new string[5];
- 其次,我们定义一个索引器
public string this[int index]
public后string
属性是和数组的数据类型相同,this
是必要的关键字!int index
表示我们需要的角标存储值 - 索引器内get和set方法和属性赋值取值一样,
get
返回arr[index]
数组上的第index个值,set
方法赋值为value
,这里注意了这个值务必写成value
!
习题7 索引器的使用
**(1)编写一个类IndexTest,定义一个长度为5的字符串型数组arr,定义一个形参为int类型的索引器实现对数组arr的读写操作。
(2)在Program类的Main方法中,实例化一个IndexTest对象,使用索引器实现对arr的读写操作。
**
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication
{
class IndexTest //自定义类
{
string[] arr = new string[5];//定义数组
public string this[int index]//定义索引器
{
get
{
return arr[index];
}
set
{
arr[index] = value;
}
}
}
class Program //主类
{
static void Main(string[] args) //主方法
{
IndexTest it = new IndexTest();
for (int i = 0; i < 5; i++)
{
Console.Write("数组元素{0}:",i+1);
it[i] = Console.ReadLine();
}
for (int i = 0; i < 5; i++)
{
Console.WriteLine(it[i]);
}
}
}
}
附加练习
类的索引器读写练习
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/**
* 类的索引器
*/
namespace Demo5
{
class Book
{
string bid;
public string Bid //读写属性
{
get { return bid; }
set { bid = value; }
}
string bname;
public string Bname
{
get { return bname; }
set { bname = value; }
}
}
class Library
{
Book[] book;
public Library(int len)
{
book = new Book[len];
}
public Book this[int index]//类数组索引器
{
get
{
return book[index];
}
set
{
book[index] = value;
}
}
}
class 类索引器
{
static void main(string[] args)
{
int length = int.Parse(Console.ReadLine());
Library lib = new Library(length);
for (int i = 0; i < length; i++)
{
Book b = new Book();
b.Bid = Console.ReadLine();
b.Bname = Console.ReadLine();
lib[i] = b;
}
for (int i=0; i<length; i++)
{
Console.WriteLine(lib[i].Bid + " " + lib[i].Bname);
}
}
}
}
继承
- 继承的内容相对简单,我们举个例子,我们创建了一个形状(shape)的类,这个类中只定义了形状的名字
class shape{
public string name;
}
- 继承定义
子类 : 父类
class son : father
子类son继承于father - 但是当我们想要去使用这么一个形状时候,我们想把他设置成矩形,因为我们矩形是有长和宽的,这时候我们应该再加入两个变量长和宽,但是我们还想让矩形是继承于形状的,我们该怎么做呢?
class rect : shape { //设置类的继承!!!使用 :
int a,b; //定义长和宽
//同样我们这里也一样继承父类中name属性,以供使用
}
- 同样继承可以继承父类中所有的内容(包括成员方法,成员变量)同样可以重写父类中的内容(成员方法,成员变量)
我们通过一道例题来理解下继承是怎么运作的
习题8 继承
**
(1)定义一个矩形类Rectangle,包含受保护的长和宽两个成员字段,计算面积的方法,构造函数实现对长和宽的初始化。
(2)编写一个长方体类Rectangular,继承自矩形类,具有高度成员变量,计算体积的方法,构造函数实现对长、宽和高度的初始化。
(3)在类Program的Main方法中,计算长度为10,宽度为20,高度为5的长方体的底面积和体积。
**
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication
{
class Rectangle
{
protected double length;
protected double width;
public Rectangle(double length, double width)
{
this.length = length;
this.width = width;
}
public double GetArea()
{
return length * width;
}
}
class Rectangular : Rectangle
{
public double height;
public Rectangular(double length, double width, double height):base(length, width)
//重点1 下面详解
{
this.height = height;
}
new public double GetArea(){
//重点2 下面详解
return 2*(length*width + width*height + length*height);
}
public double GetVolume()
{
return length * width * height;
}
}
class Program
{
static void Main(String[] args)
{
Rectangular cft = new Rectangular(10, 20, 5);
Console.WriteLine("长方体的底面积:" + cft.GetArea());
Console.WriteLine("长方体的体积:" + cft.GetVolume());
}
}
}
-
重点1 子类使用base关键字调用父类构造函数
因为C#中子类不能继承父类的构造方法,所以我们在子类构造方法中使用base关键字来实现父类中的构造方法,完成成员变量的初始化。
这里我们长方体的构造方法初始化赋值长方形的长和宽,和长方体的高,我们通过使用父类长方形中的构造方法,完成长和宽的初始化,然后再子类中完成长方体的高的初始化赋值。 -
重点2 使用new关键字隐藏父类成员
我们长方体子类继承于长方形父类,因为我们长方形计算面积是长*宽,但是我们长方体的面积就是6个面的面积和,所以我们要重写父类求面积方法并隐藏父类中求面积方法
这时我们使用new关键字来隐藏父类方法,并掩盖父类方法,成为子类独立方法,类似于多态重写(override)。
习题9 继承练习
**
(1)定义一个汽车类Vehicle,包含的字段有车轮个数wheels和车重weight,构造函数实现对wheels和weight的初始化,打印汽车信息的ShowVehicle方法。
(2)小车类Car是Vehicle的子类,包含的字段有载客人数loader,构造函数实现对wheels、weight和loader的初始化,打印小车信息的ShowCar方法。
(3)卡车类Truck是Car类的子类,包含的字段有载重量payload,构造函数实现对wheels、weight、loader和payload的初始化,打印卡车信息的ShowTruck方法。
(4)在类Program的Main方法中,分别实例化Vehicle(4个车轮,1000kg车重)、Car(4个车轮,1500kg车重,载客4人)和Truck(6个车轮,2000kg车重,载客2人,载重量4000kg)对象,打印相应的信息。
**
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication
{
class Vehicle
{
public int wheels;
public double weight;
public Vehicle(int w1, double w2)
{
wheels = w1;
weight = w2;
}
public void ShowVehicle()
{
Console.WriteLine("Vehicle 车轮:" + wheels +"; 车重:" + weight);
}
}
class Car : Vehicle
{
public int loader;
public Car(int l, int w1, double w2) : base(w1, w2)
{
loader = l;
}
public void ShowCar()
{
Console.WriteLine("Car 车轮:" + wheels +"; 车重:" + weight + "; 人数:" + loader);
}
}
class Truck : Car
{
public double payload;
public Truck(double p, int l, int w1, double w2) : base(l, w1, w2)
{
payload = p;
loader = l;
}
public void ShowTruck()
{
Console.WriteLine("Truck 车轮:" + wheels + "; 车重:" +weight + "; 人数:" + loader + "; 载重量:" + payload);
}
}
class Program
{
static void Main(String[] args)
{
Vehicle v = new Vehicle(4, 1000);
v.ShowVehicle();
Car c = new Car(4, 4 , 1500);
c.ShowCar();
Truck t = new Truck(4000, 2, 6, 2000);
t.ShowTruck();
}
}
}
代码内容均差不多,如果有不懂得内容,可以留言评论或者添加我的个人微信,欢迎大家向我提问。
多态
- 我们按照学校内容来说说多态的内容,按照我的理解,抽象类接口不应该算作多态里,抽象类接口能做得事情比仅仅多态的能力大很多,哈哈,我们这里还是按照学校内容说,所以这里我们同样把多态的内容分成3个章节来说,虚方法,抽象类,接口
-
虚方法
由于我们在父类中实现的方法要在子类中重写,所以我们要在父类中需要重写的方法前面加上virtual关键字,这样可以让子类中了解到父类中的这个方法是可以重写的ww,我们在子类中使用override关键字来重写父类中的虚方法
虚方法定义
//父类中虚方法的声明
public virtual 方法名(参数);
//子类中虚方法的重写
public override 方法名(参数);
我们来通过一个例题来了解下虚方法的定义实现和运用,仔细看看代码和注释!
习题10 虚方法的定义实现和运用
**
1.创建
People类,该类中包含:成员变量:姓名(name);虚方法:问好(SpeakHello),输出“Hello”。
2.创建ChinesePeople类,是People的子类,按中国方式重写父类方法SpeakHello 输出“某某说:你好,吃了吗”,构造方法ChinesePeople(string name)实现父类中name的初始化,方法ChineseGongfu输出“中国功夫”。
3.创建AmericanPeople类,是People的子类,按美国方式重写父类方法SpeakHello 输出“某某say:How are you?”,构造方法AmericanPeople(string name)实现父类中name的初始化,方法AmericanBoxing输出“美国拳击”。
4.在Program类的Main方法中,分别使用“Liming”和“Tom”创ChinesePeople和AmericanPeople对象,测试程序。
**
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication
{
public class People //定义父类
{
protected string name;
public virtual void SpeakHello() //定义父类中的虚方法
{
Console.WriteLine("Hello");
}
}
class ChinesePeople : People //定义子类 继承于父类People
{
public ChinesePeople(string name)//构造方法
{
this.name = name;
}
public override void SpeakHello() //重写父类中的虚方法
{
Console.WriteLine(name + "说:你好,吃了么?");
}
public void ChineseGongfu() //定义子类中私有public方法
{
Console.WriteLine("中国功夫");
}
}
class AmericanPeople : People //定义子类 继承于主类People
{
public AmericanPeople(String name) //定义构造方法
{
this.name = name;
}
public override void SpeakHello() //重写父类虚方法
{
Console.WriteLine(name + " says:How are you?");
}
public void AmericanBoxing() //定义子类私有Public方法
{
Console.WriteLine("美国拳击");
}
}
class Program
{
static void Main(string[] args) //住方法
{
ChinesePeople chinese = new ChinesePeople("Liming"); //实例化
chinese.SpeakHello(); //调用子类重写方法
chinese.ChineseGongfu();
AmericanPeople american = new AmericanPeople("Tom"); //实例化
american.SpeakHello();//调用子类重写方法
american.AmericanBoxing();
}
}
}
多态:参数不同,相同的类执行的功能不同。多态是一项让程序员将改变的事物与未改变的事物分离分开来的重要特征。只有普通的方法调用可以是多态的,构造器并不具备多态性(构造器实际是static方法,只不过该static声明是隐性的)
-
抽象类 接口OOP
我们面向对象编程里面两种实现OOP(Object Oriented Programming)编程的方法:抽象,接口。
final(不能被继承)与abstract(只能被继承)永远不能同时修饰类。 -
抽象类
1.抽象方法:只有声明,没有实现。
2.抽象表示:abstract void fun();
3.抽象方法必须用abstract修饰。如果一个类含有抽象方法,由此类为抽象 类,抽象类必须用abstract修改。
4.包含抽象方法的类。但是一个类若不包含抽象方法,用abstract修饰也是抽象类。但是如果一个抽象类中没有抽象方法,抽象类的设计即没有意义。
5.因为抽象类中无具体方法,所以不能进行实例化。
6.所以抽象类是为继承而生的,如果定义了抽象类而不去继承它,则创建抽象类的则没有意义。
7.定义一个父类,若父类中一个方法在父类中没有实现的意义,必须通过子类完成各种实现,则可将此方法定义成抽象方法,则这个父类为抽象类。 -
抽象类的目的
- 抽象类就是为了继承而存在的,为子类提供一个公共的类型;
- 封装子类中的重复内容(成员变量和方法);
- 定义抽象方法,子类虽然有不同的实现,但该方法的定义是一致的。
- 抽象类中包含抽象方法,抽象方法的声明格式为:
public abstract void cry();
抽象方法是一种特殊的方法:它只有声明,而没有具体的实现
抽象类例子:
abstract class Animal {
public abstract void cry();
}
-
接口
接口类说白了,就是一个类的模板,一个类的规定,如果你属于这类,你就必须遵循我的规定,少一个都不行,但是具体你怎么去做,我不管,那是你的事,哈哈。
接口是抽象方法的集合,是一种形式,一种标准,就像契约模式,如果实现了这个接口,那么就必须确保使用这些方法。接口中的方法必须都是抽象方法,并且接口中所有的方法不能有具体的实现,接口中成员变量只能是public static final这种形式,相当于常量,不可以修改的(一般不在接口中定义成员变量)
接口类 说的太不严谨了,应该添加引号,需要知道接口并不是类。不过说接口是模板比较正确。
- 但我认为 接口是对行为的抽象。什么叫行为的抽象?那先要说说行为。类是对现实的抽象嘛,所以用现实来举个例子。
人的说话是不是天生的?自然不是,如果天生会说话,那一般出现在重生小说里面。说话需要通过后天学习,如果不学习(例如新闻中被狼收养的孩子)就不会说话(狼孩只会狼嚎)。在这里,说话就是一种行为,是通过学习,通过思想支配表现出来的。可以说,说(人)话就是一个接口,正常人实现了该接口,所以能够说话,而狼孩实现的是狼嚎接口,所以他会狼嚎,而不会说话。
这个例子的结论是,行为是人非天生的(不能直接继承的),需要通过后天学习(继承接口)的外在活动。
通过对行为的抽象,体现在面向对象语言中,那就是接口,它是不适合在类中直接体现(或用来描述类)(类又可能需要)的方法的集合。
-
接口中可包含变量和方法。变量只能用(默认也是,其他修饰编译报错)public static final修饰。方法只能是(默认也是,其他修饰编译报错)public abstract修饰。一般不在接口中定义变量。
-
接口的注意事项
(1) 类中实现的方法必须与接口中声明的方法保持一致,如返回类型,参数列表
(2)继承接口的类必须实现接口中声明的所有方法
(3)接口是不能被实例化的,接口中声明的所有抽象方法都必须由子类实现
哈哈哈,解释的也比较抽象,不明白想要学习的,欢迎来和我私聊,嘿嘿
- 我们来通过两道例题来看看抽象类、接口是怎么定义和实现
习题11 抽象类习题练习
**
(1)编写一个抽象类Shape,该类具有两个double类型字段:周长length和面积area,具有两个抽象的方法:计算周长GetLength()和计算面积GetArea()。
(2)圆形类Circle继承类Shape,成员变量radius表示半径,构造函数实现radius的初始化,实现抽象类Shape中的两个抽象方法。
(3)编写一个锥体类Cone,里面包含两个成员变量:Shape类型的底面bottom和double类型的高height,方法GetVolume计算体积(体积=底面积*高/3)构造函数实现成员变量的初始化。
(4)在Program类中,包含一个静态的方法void Compute(Shape s),通过该方法能够计算并输出一切图形的周长和面积;在Main函数中调用Compute法,计算并输出半径为5的圆形的周长和面积,并算出以该圆形为底,高度为10的锥体的体积。
**
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication
{
public abstract class Shape //定义一个抽象类
{
public double length, area;
public abstract double GetLength(); //定义抽象方法
public abstract double GetArea();//定义抽象方法
}
class Circle : Shape//定义子类继承于抽象类shape
{
double radius;
public Circle(double radius) //定义构造方法
{
this.radius = radius;
}
public override double GetLength() //重写父类抽象方法
{
length = 2 * 3.14 * radius;
return length;
}
public override double GetArea() //重写父类抽象方法
{
area = 3.14 * radius * radius;
return area;
}
}
class Cone //定义自定义类
{
Shape bottom;
double height;
public Cone(Shape bottom, double height)
{
this.bottom = bottom;
this.height = height;
}
public double GetVolume()
{
return bottom.GetArea() * height / 3;
}
}
class Program //主类
{
public static void Compute(Shape s) //主类私有方法 传入参数为抽象类
{
Console.WriteLine("周长为:" + s.GetLength());
Console.WriteLine("面积为:" + s.GetArea());
}
static void Main(string[] args) //主方法
{
Circle c = new Circle(5); //实例化
Console.WriteLine("创建一个底面为圆形的锥体");
Cone cone = new Cone(c, 10);
Compute(c);
Console.WriteLine("体积为:" + cone.GetVolume());
}
}
}
重点
- 在子类继承抽象父类时,使用override重写父类的抽象方法
习题12 接口 使用接口写个计算器,完成加减乘除运算。
**
(1)定义接口ICompute含有一个方法int Compute(int m,int n)。
(2)设计Add、Subtract、Multiply和Divide四个类分别实现此接口,完加减乘除运算。
(3)设计一个类UseCompute,含有方法:
public void UseCom(ICompute com, int one, int two)
要求:
1.用传递过来的对象调用接口的Compute方法完成运算;
2.输出运算的结果。
(4)在Program类的Main方法中,调用UseCompute中的方法UseCom来完成25和5的加减乘除运算。
**
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication
{
interface ICompute //定义接口
{
int Compute(int m, int n); //定义接口内的方法
}
class Add : ICompute //定义自定义类实现接口
{
public int Compute(int m, int n) //实现接口内的方法
{
return n + m;
}
}
class Subtract : ICompute//定义自定义类实现接口
{
public int Compute(int m, int n)//实现接口内的方法
{
return m - n;
}
}
class Multiply : ICompute//定义自定义类实现接口
{
public int Compute(int m, int n)//实现接口内的方法
{
return n * m;
}
}
class Divide : ICompute//定义自定义类实现接口
{
public int Compute(int m, int n)//实现接口内的方法
{
return m / n;
}
}
class UseCompute //自定义了UseCompute
{
public void UseCom(ICompute com, int one, int two)
{
int result = com.Compute(one, two);
Console.WriteLine("结果是" + result);
}
}
class Program//主类
{
static void Main(string[] args)//住方法
{
Add add = new Add();
Subtract sub = new Subtract();
Multiply mul = new Multiply();
Divide div = new Divide();
UseCompute c = new UseCompute();
//实例化对象
c.UseCom(add, 25, 5);
c.UseCom(sub, 25, 5);
c.UseCom(mul, 25, 5);
c.UseCom(div, 25, 5);
}
}
}
网友评论
首先,零基础,应该是针对学过面向对象编程语言的人但是没接触过c sharp的人而言的,若是真正纯零基础,鬼会知道这里面继承讲的是什么。
其次,“精通”。以下是个人见解:谈到精通,至少需要提及这门语言的独特之处,然而文章中并没有,只是提到了面向对象编程该有的东西,内容稍微换一换,拿去讲java未免不可,说是精通,未免夸张。
标题取成这样,不知道误导了多少真正小白……
可能认真我就输了吧~
“c sharp语言一小时,从入门到放弃系列”
🙃