介绍
本博客将会以任务为驱动,介绍C#中的接口、委托的相关知识
作业内容
实现对某鸭工厂的产品生产统一管理,主要产品包括鸭脖和鸭翅。武汉工厂能生生产鸭脖和鸭翅,南京工厂只能生产鸭翅,长沙工厂只能生产鸭脖。具体要求如下:
- 定义接口 IProductionFactory,包含生产鸭脖和鸭翅的方法。
- 定义类 WuhanFactory、NanjingFactory、ChangshaFactory 分别实现接口 IProductionFactory,用于具体的生产工厂。
- 使用委托 ProductionDelegate 定义生产委托。
- 在 Main 函数中,创建不同工厂的实例,并通过生产委托进行生产。
接口
接口的意义
在初学接口的时候,难免会觉得接口是一个多余的功能。因为我们如果想在一个类中实现一个方法,直接就在这个类中实现这个方法就是了,为什么还要多写几行代码去声明接口然后再去类中调用呢?
这里就涉及到一个编程的规范问题。举一个最深刻的例子:
先抛开实际解决方案,这里仅做一个”不恰当“的例子.
当我们使用手机和电脑或者其他设备浏览网页时,为了能适配各个设备,网页呈现给我们的样式是不一样的。
而我们想要去给网页做适应的时候,需要写一个方法为各个设备找到最好的呈现方式,如果不用接口的话,我们写出来的适配函数可能长这样:
public void AndroidDisplay(){}
public void IphoneDisplay(){}
public void IpadDisplay(){}
……
看起来是毫无关系的,但是这些方法都是实现了页面显示的方法。
如果我们采用接口,方法的实现就会好很多:
public interface Display{ }
这里接口的意义就很明显了,接口的意义就是方便统一管理、方便调用。
同时,当我们想使用这个方法的时候,就不同继承一整个父类,直接调用这个接口就行了。
实现及实现格式
接口格式:
(无需声明接口的访问类型) interface (接口名字){
void function();
(无实现代码的函数)
}
接口本来就是一个约定类型,可以公开访问,不能进行私有的实现。
在接口中,我们要注意一下几点:
- 接口声明不能包含静态成员和数据成员
- 接口方法有任何实现代码
- 接口也可以继承接口
以下是实现代码:
interface IProductionFactory
{
void DuckNeck();
void DuckWing();
}
类调用接口
在类中调用接口和类继承父类类似,但是需要注意的是,在C#中,一个类只能有一个父类,这称谓单继承。而接口可以无限制调用,这也是接口的优点。
有一点需要注意的是,接口中的方法必须实现,不能不写。
public class ClassExample : InterfaceExample
{
public void function(){
(对方法的实现)
}
}
可以看到,我们对接口方法的实现是在类里面完成的。
以下是实现代码:
public class WuhanFactory : IProductionFactory
{
public void DuckNeck()
{
Console.WriteLine("武汉工厂生成鸭脖");
}
public void DuckWing()
{
Console.WriteLine("武汉工厂生产鸭翅");
}
}
补充:as
在C#中,我们可以通过强制类型转换来进行类的转换,这个时候接口的作用就更加明显了——在进行类的转换之后,我们的代码无需进行更改(前提是前后两个类都有对该接口的实现)。但是如果转换失败,那么程序会直接抛出异常,我们这个时候就可以使用as来进行类型转换。
as进行类型转换的时候,如果转换成功,会直接赋值这个类的转换,如果失败,会赋值null
举例
public interface IShape
{
void Draw();
}
public class Circle : IShape
{
public void Draw()
{
Console.WriteLine("Drawing a circle");
}
}
public class Rectangle : IShape
{
public void Draw()
{
Console.WriteLine("Drawing a rectangle");
}
}
public class Program
{
public static void DrawShape(IShape shape)
{
shape.Draw();
}
public static void Main(string[] args)
{
IShape shape2 = new Rectangle();
Circle circle2 = shape2 as Circle;
if (circle2 != null)
{
Console.WriteLine("shape2 is a Circle");
}
else
{
Console.WriteLine("shape2 is not a Circle");
}
}
}
以上代码中,我们对shape2
进行类型转换,不难发现,我们的类型转换是失败的,但是程序并没有因此抛出异常,而是打印出shape2 is not a Circle
,这里就体现出as的作用。
委托
C#中的委托可以简单理解为是C++中的函数指针。
委托就是一个方法列表,使用一次委托就是对各个方法进行一次调用。
实现及格式代码
以下是实现代码:
public delegate void ProductionDelegate();
注意:
委托必须是返回值和变量类型与你想调用的函数一致
关于委托的返回值
在进行委托组合的时候,最后一个方法的返回值就是委托的返回值。而在 委托中的其他函数则是忽略其返回值的。
ref
ref
说到底,就是类似于cpp中的&
如果我们想通过委托来修改一个变量的值,那么就需要使用ref
来进行引用参数声明。
但是在所有环节里面都要用到ref
delegate int muti(ref int x);
public static int f3(ref int x)
{
x = x * x;
Console.WriteLine(x);
return x;
}
muti(ref x);
有一点需要注意的是仅修改变量,不是利用返回变量值来进行修改。
关于变量
在委托的方法中无法使用和初始化与方法中相同名称的局部变量,因为委托方法被转换为一个独立的类,其执行上下文和方法中的局部变量是分离的。要在委托方法中使用方法中的局部变量,可以通过参数传递或定义为方法的字段或属性来共享这些变量。
匿名方法
在有的方法中,我们只是用该方法一次。我们可以使用匿名方法来简化函数声明
muti muti = new muti(f3);
muti += delegate(ref int x){
x=x+20;
return x;
};
抛出异常
在题目中,当调用南京工厂的鸭脖
时候,我们就可以抛出异常,因为这不是我们在程序设计中想让他人使用的类方法(鸭脖
是武汉工厂和长沙工厂的核心科技)
实现
throw new NotSupportedException("南京工厂不生产鸭脖");
- throw 语句用于在代码中主动引发异常。它允许开发人员在程序的某个位置显式地抛出一个异常,以指示出现了无法处理的错误或不支持的操作。
- NotSupportedException 是 .NET Framework 提供的一个异常类,它表示某个操作、方法或功能不受支持。当程序尝试执行一个不受支持的操作时,可以抛出这个异常来通知调用者。