接口
什么是接口
说明:接口也是一个类,但是接口里只包含没有实现的方法
习惯上,接口用 I 来打头,表示他是一个接口,而不是一个类
定义接口
public interface IFlyHandler{
public void Fly();
}
实现接口
public class Type1Enemy:IFlyHandler{
}
接口指定了一个方法,但是具体是什么方法,接口是不管的,在实现接口的时候,方法就实现了
定义⼀个接⼝在语法上跟定义⼀个抽象类完全相同,但不允许提供接⼝中任何成员的实现
⽅式
接口不能有构造函数
也不能有字段,不允许运算符重载
创建接口的操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IU6sCG7o-1680780500883)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230406165142267.png)]
右键选择新建项而不是类,然后选择接口,注意开头用I标注
创建接口的代码
using System;
using System.Collections.Generic;
using System.Text;
namespace 接口
{
interface IFly
{
//一个接口可以包含多个方法
void Fly();//即使不加 public 也默认是一个public属性的函数
void FlyAttack();
//接口只包含函数的声明,不包含函数的函数体
}
}
接口实现的操作
创建两个类来实现接口的方法[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HedBMW9P-1680780500885)(C:\Users\CoreDawg\AppData\Roaming\Typora\typora-user-images\image-20230406170036773.png)]
冒号继承之后,点击灯泡 - 实现接口
就能自动帮助实现
namespace 接口
{
class Plain : IFly
{
public void Fly()
{
Console.WriteLine("飞机飞行");
}
public void FlyAttack()
{
Console.WriteLine("飞机攻击");
}
}
}
class Bird : IFly
{
public void Fly()
{
Console.WriteLine("小鸟飞行");
}
public void FlyAttack()
{
Console.WriteLine("小鸟攻击");
}
}
使用接口
单独使用plain的接口
static void Main(string[] args)
{
//单独使用飞机的接口
Plain p = new Plain();
p.Fly();
p.FlyAttack();
//输出 飞机飞行 飞机攻击
}
使用接口声明对象
//使用接口声明对象
IFly fly;
fly = new Plain();//把飞机赋值给接口声明的对象,这样这个对象就有了飞机的特性
fly.Fly();
fly.FlyAttack();
//输出 飞机飞行 飞机攻击
使用接口声明的对象,需要指定具体是哪个接口,因为接口之间的功能名是重复的
多态
//使用接口声明对象
IFly fly;
fly = new Plain();//把飞机赋值给接口声明的对象,这样这个对象就有了飞机的特性
fly.Fly();
fly.FlyAttack();
//输出 飞机飞行 飞机攻击
fly = new Bird();
fly.Fly();
fly.FlyAttack();
//输出 小鸟飞行 小鸟攻击
接口的形态发生了变化
接口的继承
接⼝可以彼此继承,其⽅式和类的继承⽅式相同
public interface A{
void Method1();
}
public interface B:A{
void Method2();
}
演示:
接口1
interface Interface1
{
void Method1();
}
接口2可以继承接口1
interface Interface2:Interface1//接口的继承
{
void Method2();
}
这时候接口2就有了接口1的方法声明
这样的话实现的话,就要同时实现两个方法
class ClassInterface : Interface2
{
public void Method1()
{
throw new NotImplementedException();
}
public void Method2()
{
throw new NotImplementedException();
}
}
类可以实现多个接口
class ClassInterface : Interface2,Interface1//实现多个接口
什么是索引器
通过索引访问变量的操作
//演示数组
int[] array = { 123, 22, 33, 55, 92, 9, 1, 3, 13, };
//通过索引访问变量
Console.WriteLine(array[1]);
//使用索引器赋值
定义索引器
public int this[int index]
{
get//取值 调用
{
return 100;//需要返回值
}
set//赋值
{
//value 设置的时候会传递value这个参数
Console.WriteLine(value);
}
}
索引调用的演示
Test t = new Test();
t[9] = 200;//当你使用索引的时候,会把索引给到index
输出调用的索引
public int this[int index]
{
get//取值 调用
{
Console.WriteLine(index);
return 100;//需要返回值
}
set//赋值
{
//value 设置的时候会传递value这个参数
Console.WriteLine(value);
Console.WriteLine(index);
}
}
用索引取值
Console.WriteLine(t[9]);
索引器的作用
在类里先声明一个数组
//长度为10的名字 字符串数组
public string[] name = new string[10];
可以利用索引器往这个里面赋值,也可以利用索引器往这个里面取值
public string this[int index]
{
get
{
return name[index];
}
set
{
name[index] = value;//把用户给到的value设定到用户取值的位置
}
}
//Test就相当于一个数组管理器了
Test t = new Test();
t[0] = "张三";
t[1] = "李四";
//取值
Console.WriteLine(t[1]);
Console.WriteLine(t[0]);
直接定义一个字符串数组更方便
string[] name = new string[10];
name[0] = "sahidha";
name[1] = "16513";
星期和数字转换器
先定义一个数组保存星期几的英文字符
class Week
{
private string[] days = { "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat", "Sun" };//索引加1就是周几
public int GetDay(string day)
{
int i = 0;
foreach(string temp in days)
{
if (temp == day) return i+1;
i++;
}
return -1;
}
}
使用
Week w = new Week();
Console.WriteLine( w.GetDay("Thurs"));
使用索引器
public int this[string day]
{
get
{
return GetDay(day);
}
}
Week w = new Week();
Console.WriteLine( w["Thurs"]);
什么是运算符重载
什么是运算符?
± * / %
设定个学生的构造函数和成员
class Student
{
private int age;
private string name;
private long id;
public Student(int age, string name, long id)
{
this.age = age;
this.name = name;
this.id = id;
}
}
判断两个数据完全一样的学生是否相等
static void Main(string[] args)
{
//s1 s2 数据完全相等
Student s1 = new Student(20, "shdiah", 32512);
Student s2 = new Student(20, "shdiah", 32512);
//判断s1 s2是否相等
Console.WriteLine(s1 == s2);//得到结果False
}
说明:变量在创建对象的时候在 堆 里创建了两个对象
在 栈 里存储的两个地址也不一样
在做比较的时候比较的是地址,故得到False
那么直接吧s1赋值给s3呢,这样的话两个对象一样吗
Student s3 = s1;
Console.WriteLine(s1 == s3);//得到结果True
说明:两个等号比较的时候比较的就是引用地址,如果s3和s1的引用一样,故得到True
运算重载符的例子
以上的比较都是比较的引用地址,那么如何比较两个的对象的数据呢,这时候就需要用到运算重载符
//运算重载符的书写
public static bool operator==(Student s1,Student s2)//需要重写的符号要写在关键字 operator 后面
{
//用三个条件去比数据
if (s1.age == s2.age && s1.id == s2.id && s1.name == s2.name)
{
return true;
}
return false;
}
==和!=需要成对的书写
public static bool operator!=(Student s1, Student s2)
{
//可以直接调用上面的函数判断
bool result = s1 == s2;
return !result;
}
对比方法重新调用
Console.WriteLine(s1 == s2);
面向对象练习题1
*1.C#中的方法重写使用关键字()。*
override
*2. 以下的C#代码,试图用来定义一个接口:*
*public interface IFile{*
*int A;*
*int delFile()* *{*
*A = 3;*
*}*
*void disFile();*
*}*
*关于以上的代码,以下描述错误的是(d)。(选择一项)*
a) 以上的代码中存在的错误包括:不能在接口中定义变量,所以int A代码行将出现错误
b) 以上的代码中存在的错误包括:接口方法delFile是不允许实现的,所以不能编写具体的实现函数
c) 代码void disFile();定义无错误,接口可以没有返回值
d) 代码void disFile();应该编写为void disFile(){};
*3.在C#中,接口与抽象基类的区别在于(a)。*
a) 抽象基类可以包含非抽象方法,而接口只能包含抽象方法
b) 抽象基类可以被实例化,而接口不能被实例化
c) 抽象基类不能被实例化,而接口可以被实例化
d) 抽象基类就是接口,它们之间无差别
*4.在开发某图书馆的图书信息管理系统的过程中,开始为教材类图书建立一个TextBook类;现在又增加了杂志类图书,于是需要改变设计,则下面最好的设计应该是(c)。*
a) 建立一个新的杂志类Journal。
b) 建立一个新的杂志类Journal,并继承TextBoook类。
c) 建立一个基类Book和一个新的杂志类Journal,并让Journal类和TextBoook类都继承于Book类。
d) 不建立任何类,把杂志图书的某些特殊属性加到TextBoook类中。
*5.可以使用(override)关键字重写方法。*
a) Override
b) New
*6**.以下叙述正确的是:(bc)*
A.接口中可以有虚方法。 B.一个类可以实现多个接口。
C.接口不能被实例化。 D.接口中可以包含已实现的方法。
*7**.下列代码输出为( b):*
class Father{
public void F() { Console.WriteLine(“A.F”); }
public virtual void G() { Console.WriteLine(“A.G”); }
}
class Son: Father{
new public void F() { Console.WriteLine(“B.F”); }
public override void G() { Console.WriteLine(“B.G”); }
}
class override_new{
static void Main() {
Son b = new Son();
Father a = b;
a.F();
b.F();
a.G();
b.G();
}
}
A. A.F B.F A.G B.G
B. A.F B.F B.G B.G
C. A.F A.F B.G B.G
D. B.F B.F B.G B.G
*8**.写出程序的输出结果:*
public class A
{
public virtual void Fun1(int i)
{
Console.WriteLine(i);
}
public void Fun2(A a)
{
a.Fun1(1);
Fun1(5);
}
}
public class B:A {
public override void Fun1(int i)
{
base.Fun1(i+1);
}
public static void Main() {
B b=new B();
A a=new A();
a.Fun2(b);
b.Fun2(a);
}
}
答案:2 5 1 6
*9**.在C#中,允许多接口继承,从多个接口时,使用“:”后跟继承的接口的名字,多个接口名称之间使用(c)符号进行分割。(选择一项)*
a)”.’
b)”->”
c)”,”
d)”::”
*1**0**.阅读以下的C#代码:*
class A
{
public virtual void printStr(string str)
{
Console.WriteLine(str);
}
}
class B:A
{
public override void printStr(string str)
{
str=str+" 重写的方法";
Console.WriteLine(str);
}
}
class DefaultInitializerApp
{
public static void Main()
{
B b=new B();
A a=b;
a.printStr(“打印”);
b.printStr(“打印”);
Console.ReadLine();
}
}
运行程序后将在控制台窗口打印(a )。(选择一项)
a. 打印 重写的方法 打印 重写的方法
b. 打印 打印 重写的方法
c. 打印
d. 程序有误,不能运行
*1**1**.在C#的语法中,(a)关键字可以实现在派生类中对基类的虚函数进行重载(选一项)*
a> override
b> new
c> static
d> virtual
*1**2**.请问,此程序输出结果是©*
abstract class BaseClass{
public virtual void MethodA(){Console.WriteLine(“BaseClass”);}
public virtual void MethodB(){}
}
class Class1: BaseClass{
public void MethodA(){Console.WriteLine(“Class1”);}
public override void MethodB(){}
}
class Class2: Class1{
new public void MethodB(){}
}
class MainClass{
public static void Main(string[] args){Class2 o = new Class2();o.MethodA(); }
}
A. BaseClass
B. BassClass Class1
C. Class1
D. Class1 BassClass
*1**3**.请问,此程序输出结果是(a)*
public abstract class A { public A() { Console.WriteLine(‘A’); }
public virtual void Fun() { Console.WriteLine(“A.Fun()”); } }
public class B: A { public B() { Console.WriteLine(‘B’); }
public new void Fun() { Console.WriteLine(“B.Fun()”); }
public static void Main() { A a = new B();a.Fun(); } }
A. A B A.Fun()
B. A B B.Fun()
C. B A A.Fun()
D. B A B.Fun()
*14**.以下叙述正确的是(bc):*
A. 接口中可以有虚方法。
B. 一个类可以实现多个接口。
C. 接口不能被实例化。
D. 接口中可以包含已实现的方法。
*15**.以下关于C#代码的说法正确的是(a)。(选择一项)*
Public abstract class Animal
{
Public abstract void Eat();
Public void Sleep()
{
}
}
a. 该段代码正确
b. 代码错误,因为类中存在非抽象方法
c. 代码错误,因为类中的方法没有实现
d. 通过代码“Animal an = new Animal();”可以创建一个Animal对象
*16**.在c#中,关于继承和接口,以下说法正确的是() (b)*
a. c#允许许多接口实现,也允许多重继承
b.c#允许多接口实现,但不允许多重继承
c.c#不允许多接口实现,但允许多重继承
d.c#不允许多重接口实现,也不允许多重继承
*17.* *在C#中,已知下列代码的运行结果是“老虎吃动物”,请问在空白处1和空白处2*
*分别应该填写的代码是(c
)*
Public class Animal
{
Public 空白处1 void Eat()
{
Consone.WriteLine(“我要吃”);
}
}
Public class Tiger:Animal
{
Public 空白处2 void Eat()
{
Consone.WriteLine(“老虎吃动物”);
}
Public calssTest
{
Static void Main()
{
Animal an = new Tiger();
an.Eat;
}
}
}
a) Virtual , new
b) override , virtual
c) virtual , override
d) new , virtual
*18.* *在C#中,下列关于抽象类和接口的说法,正确的是)(b)(选择一项)*
a) 在抽象类中,所以的方法都是抽象方法
b) 继承自抽象类的子类必须实现起父类(抽象类)中的所以抽象方法
c) 在接口中,可以有方法实现,在抽象类中不能有方法实现
d) 一个类可以从多个接口继承,也可以从多个抽象类继承
*19.* *关于以下C#代码的说法正确的是()*
Public abstract class Animal{
Public abstract void Eat();
}
Public class Tiger:Animal{
Public override void Eat(){
Console.WriteLine(“老虎吃动物”);
}
}
Public class Tigress:Tiger{
Static void main(){
Tigress tiger=new Tigress();
Tiger.Eat();
}
}
a. 代码正确,但没有输出
b. 代码正确,并且输出为”老虎吃动物”;
c. 代码错误,因为Tigress类没有实现抽象基类Animal中的抽象方法
d. 代码错误,因为抽象基类Animal的Eat方法没有实现
结构体和类的不同
类的情况
static void Main(string[] args)
{
//创建两个类的对象
Student stu1 = new Student(18,"小芳");
Student stu2 = new Student(25,"小刚");
stu2 = stu1;
stu1.age = 30;
stu1.name = "小燕";
Console.WriteLine(stu2.name);
Console.WriteLine(stu2.age);
}
数据也跟着改变了
说明:因为s1s2都是指向同一个地址的,因此只修改一个地址的数据,两边会跟着改
结构体的情况
声明一个结构体
//定义一个结构体
struct StudentSt
{
public int age;
public string name;
//结构体里面也可以有构造函数
public StudentSt(int age, string name)
{
this.age = age;
this.name = name;
}
}
结构体的存放
StudentSt stu1 = new StudentSt(18, "小芳");//因为结构体是值类型的,所以他不会在 堆 里存放 而是在 栈 里存放
//StudentSt stu2 = new StudentSt(25, "小刚");
StudentSt stu2 = stu1;//把里面的每个数据单独赋值
//stu1 stu2 是在 栈 里面不同区域存储的
//并不是像在类里一样赋值了地址引用
//因此就算此时修改s1,也对s2,没有影响
stu1.age = 30;
stu1.name = "小燕";
Console.WriteLine(stu2.age);
Console.WriteLine(stu2.name);
//输出结果仍然是18 小芳
.name = “小燕”;
Console.WriteLine(stu2.name);
Console.WriteLine(stu2.age);
}
数据也跟着改变了
说明:因为s1s2都是指向同一个地址的,因此只修改一个地址的数据,两边会跟着改
### 结构体的情况
声明一个结构体
```C#
//定义一个结构体
struct StudentSt
{
public int age;
public string name;
//结构体里面也可以有构造函数
public StudentSt(int age, string name)
{
this.age = age;
this.name = name;
}
}
结构体的存放
StudentSt stu1 = new StudentSt(18, "小芳");//因为结构体是值类型的,所以他不会在 堆 里存放 而是在 栈 里存放
//StudentSt stu2 = new StudentSt(25, "小刚");
StudentSt stu2 = stu1;//把里面的每个数据单独赋值
//stu1 stu2 是在 栈 里面不同区域存储的
//并不是像在类里一样赋值了地址引用
//因此就算此时修改s1,也对s2,没有影响
stu1.age = 30;
stu1.name = "小燕";
Console.WriteLine(stu2.age);
Console.WriteLine(stu2.name);
//输出结果仍然是18 小芳