参考书:《 visual C# 从入门到精通》
第二部分 理解C#对象模型
第13章 创建接口和定义抽象类

文章目录

  • 13.1 理解接口
    • 13.1.11 定义接口
    • 13.1.2 实现接口
    • 13.1.3 通过接口类引用类
    • 13.1.4 使用多个接口
    • 13.1.5 显式实现接口
    • 13.1.6 接口的限制
    • 13.1.7 定义和使用接口
  • 13.2 抽象类
  • 13.3 密封类
    • 13.3.1 密封方法
    • 13.3.2 实现并使用抽象类

接口不包含任何代码或数据,它只规定了从接口继承的类必须提供哪些方法和属性。所以继承真正强大的地方就在于能从接口继承。使用接口,方法的名称/签名可以和方法的具体实现完全隔绝。

抽象类很大程度上类似于接口,但他可以包含代码和数据。可以将抽象类中的某些方法指定为虚方法,且指定从抽象类继承的类必须以自己的方式实现这些方法。

13.1 理解接口

使用接口可以真正地将whathow区分开。接口指定-有什么,即指定方法的名称、返回类型和参数,具体-怎么做,如何实现就不是接口关心的。

13.1.11 定义接口

用关键字interface,接口中不允许使用任何访问修饰符:publicprivateprotected。如下:

interface IComparable{int CompareTo(object obj);
}

接口不包含任何数据,不能向接口中添加任何字段。

13.1.2 实现接口

interface ILandBound{int NumberOfLegs();
}class Horse:ILandBound{...;public int NumberOfLegs(){return 4;}
}

实现接口时必须注意每个方法要与接口方法完全匹配:

  • 方法名和放回类型完全匹配
  • 所有参数包括关键字refout都要完全匹配
  • 实现接口的所有方法都必须具有public可访问性。如果使用显式接口实现,就不应该为方法添加访问修饰符

一个类可以在从一个类继承的同时实现接口。C#根据位置来区分,基类名在前面,接口名在后面。

interface ILandBound{...;
}
class Mammal{...;
}
class Horse:Mammal,ILandBound{..;
}

13.1.3 通过接口类引用类

我们知道基类变量能引用派生类对象,同样地接口变量也能引用实现该接口的类的对象。

Horse myHorse(...);
ILandBound imyHorse=myHourse;

可以用is操作符来验证对象是实现了指定接口的一个类的实例。

if(myHorse is ILandBound){ILandBound iLandBoundAnimal=myHorse;
}

13.1.4 使用多个接口

一个类只能有一个基类,但可以实现多个接口,没有数量限制。

class Horse:Mammal,ILandBound,IGrazable{...;
}

13.1.5 显式实现接口

如果一个类实现多个接口,其中一些接口的方法的签名相同,C#并不能区分这个类的方法实现的式哪个接口的方法,这样的话就是一个方法同时实现了多个接口的方法。

为了能区分一个方法实现的是哪个接口的方法,就需要显式实现接口:

class Horse:ILandBound,IJourney{...;int ILandBound.NumberOfLegs(){return 4;}int IJourney.NumberOfLegs(){return 3;}
}

注意到上述代码的一个细节:两个显式实现接口的方法都没有关键字public,实际上显式实现接口就不能用public,因为这两个方法对于类Horse来说是私有的。这样处理是合理的,如果是公有的话,类的对象访问方法是就没法确定调用的是哪个方法了,所以必须让它变为私有的 。

那么问题来了,我们要如何访问这两个方法呢?答案是要通过接口类引用Horse对象。

Horse horse=new Horse();
...;
IJourney journeyHorse=horse;
int legsInJourney=journeyHorse.NumberOfLegs();
ILandBound landBoundHorse=horse;
int legsOnHorse=landBoundHorse.NumberOfLegs();

13.1.6 接口的限制

接口需要注意几点:

  • 不能再接口中定义任何字段
  • 不能定义任何构造器
  • 不能定义任何析构器
  • 不能为任何方法指定访问修饰符
  • 不能再接口中嵌套任何类型
  • 一个接口可以从另一个接口继承,但不能从结构或类继承

13.1.7 定义和使用接口

下面我们运用学到的理论,创建一个简单的绘图应用程序,可以再画布上画出两种图形:鼠标左击画出正方形,右击画出圆形。

新建一个空白应用(通用Windows),先新建两个源文件定义两个接口:

IDraw.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;//手动添加namespace C_13_1_7
{interface IDraw{void SetLocation(int xCoord, int yCoord);void Draw(Canvas canvas);}
}

IColor.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI;
namespace C_13_1_7
{interface IColor{void SetColor(Color color);}
}

然后新建两个源文件定义两个类

Square.cs

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;
using Windows.UI.Xaml.Controls;
namespace C_13_1_7
{class Square:IDraw,IColor{private int sideLength;private int locX = 0, locY = 0;private Windows.UI.Xaml.Shapes.Rectangle rect = null;public Square(int sideLength){this.sideLength = sideLength;}void IDraw.Draw(Canvas canvas){if (this.rect != null)canvas.Children.Remove(this.rect);elsethis.rect = new Windows.UI.Xaml.Shapes.Rectangle();this.rect.Height = this.sideLength;this.rect.Width = this.sideLength;Canvas.SetTop(this.rect, this.locY);Canvas.SetLeft(this.rect, this.locX);canvas.Children.Add(this.rect);}void IColor.SetColor(Windows.UI.Color color){if (this.rect != null){SolidColorBrush brush = new SolidColorBrush(color);this.rect.Fill = brush;}}void IDraw.SetLocation(int xCoord, int yCoord){this.locX=xCoord;this.locY = yCoord;}}
}

Circle.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;
using Windows.UI.Xaml.Controls;
namespace C_13_1_7
{class Circle:IDraw,IColor{private int diameter;private int locX = 0, locY = 0;private Ellipse circle = null;public Circle(int diameter){this.diameter = diameter;}void IDraw.Draw(Canvas canvas){if (this.circle != null)canvas.Children.Remove(this.circle);elsethis.circle = new Ellipse();this.circle.Height = this.diameter;this.circle.Width = this.diameter;Canvas.SetTop(this.circle, this.locY);Canvas.SetLeft(this.circle, this.locX);canvas.Children.Add(this.circle);}void IColor.SetColor(Color color){if (circle != null){SolidColorBrush brush = new SolidColorBrush(color);this.circle.Fill = brush;}}void IDraw.SetLocation(int xCoord, int yCoord){this.locX=xCoord;this.locY = yCoord;}}
}

然后再设计窗口下建立如下图布局,其实就是拖入两个控件:TextBlockCanvasCanvas控件可以就命名为draw,在Canvas的事件关联中关联两个事件:Trapped事件的处理方法命名为’drawingCavas_Tapped’、RightTapped的处理方法命名为draw_RightTapped。这两个方法的实现如下:

MainPage.xaml.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.UI;
// https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x804 上介绍了“空白页”项模板namespace C_13_1_7
{/// <summary>/// 可用于自身或导航至 Frame 内部的空白页。/// </summary>public sealed partial class MainPage : Page{public MainPage(){this.InitializeComponent();}private void drawingCavas_Tapped(object sender, TappedRoutedEventArgs e){Point mouseLocation = e.GetPosition(this.draw);Square mySquare = new Square(100);if(mySquare is IDraw){IDraw drawSquare = mySquare;drawSquare.SetLocation((int)mouseLocation.X, (int)mouseLocation.Y);drawSquare.Draw(draw);}if(mySquare is IColor){IColor colorSquare = mySquare;colorSquare.SetColor(Colors.BlueViolet);}}private void draw_RightTapped(object sender, RightTappedRoutedEventArgs e){Point mouseLocation = e.GetPosition(this.draw);Circle myCircle = new Circle(100);if(myCircle is IDraw){IDraw drawCircle = myCircle;drawCircle.SetLocation((int)mouseLocation.X, (int)mouseLocation.Y);drawCircle.Draw(draw);}if(myCircle is IColor){IColor colorCircle = myCircle;colorCircle.SetColor(Colors.HotPink);}}}
}

完成以后直接可以运行了,效果如下:

这个画图程序看起来有点复杂只是因为其中运用了画图相关的指令我们很陌生,实际上回过头看看也没有很复杂,其中包括两个类一个实现画正方形一个画圆形,这两个类都实现了两个接口,意味着这两个类都必定包含两个接口中的方法。且实现接口的方法都是显式实现的,所以最后调用相关方法时需要事先用接口的实例引用两个类的对象。

这种风格的编程给人感觉好像有点啰嗦,想想我们完全可以不用接口来实现这个画图应用的,但这种做法还是有好处的,就是这样显得条理清晰,没那么容易出错,出错了也方便修改。

13.2 抽象类

通过将一个类显式声明为抽象类,明确不能创建这个类的实例。用关键字abstract来实现抽象类的声明。

abstract class GrazingMammal::Mammal,IGrazable{public abstruct void DigestGrass();...;
}

抽象类可以包含抽象方法,抽象方法与虚方法相似,但它不含方法主体,派生类必须重写该方法。抽象方法不可以私有。

13.3 密封类

如果不希望一个类作为基类使用,可以用关键字sealed防止类被用作基类。

sealed class Horse::GrazingMammal,ILandBound{...;
}

这样任何视图将它作为基类的代码都会编译出错。密封类中不能声明任何虚方法,同时抽象类不能密封。

13.3.1 密封方法

可以用sealed来声明一个非密封类中的一个单独的方法是密封的,这样派生类不能重写该方法。注意只有用override声明的方法才能密封,所以方法要声明为sealed override

区分下面四个关键字:

  • interface引入方法的名字
  • virtual是方法的第一个实现
  • override是方法的另一个实现
  • sealed是方法的最后一个实现

13.3.2 实现并使用抽象类

现在我们考虑用抽象类来重新实现前面的画图程序,前面的画图程序是用了接口,但其实用抽象类可能更好一点。抽象类的话,直接可以把一些字段包括进去,同时建立三个抽象方法,这些方法是可以实现的,后面的派生类就重载这些方法的时候可以用base直接调用抽象类中定义的方法,还是要方便很多的。

首先是抽象方法:DrawShape.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//手动添加
using Windows.UI;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;
using Windows.UI.Xaml.Controls;
namespace C_13_1_7
{abstract class DrawShape{private int size;private int locX = 0, locY = 0;protected Shape shape = null;public  DrawShape(int size){this.size = size;}public void SetLocation(int xCoord,int yCoord){this.locX = xCoord;this.locY = yCoord;}public void SetColor(Color color){if (shape != null){SolidColorBrush brush = new SolidColorBrush(color);this.shape.Fill = brush;}}public virtual void Draw(Canvas canvas){if (this.shape == null){throw new InvalidOperationException("Shape is null");}this.shape.Height = this.size;this.shape.Width = this.size;Canvas.SetTop(this.shape, this.locY);Canvas.SetLeft(this.shape, this.locX);canvas.Children.Add(this.shape);}}
}

然后两个派生类都要做相应的修改:

Square.cs

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;
using Windows.UI.Xaml.Controls;
namespace C_13_1_7
{class Square : DrawShape{ public Square(int sideLength) : base(sideLength){}public override void Draw(Canvas canvas){if (this.shape != null){canvas.Children.Remove(this.shape);}else{this.shape = new Windows.UI.Xaml.Shapes.Rectangle();}base.Draw(canvas);}}
}

Circle.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;
using Windows.UI.Xaml.Controls;
namespace C_13_1_7
{class Circle:DrawShape{public Circle(int diameter) : base(diameter){}public override void Draw(Canvas canvas){if (this.shape != null){canvas.Children.Remove(this.shape);}else{this.shape = new Ellipse();}base.Draw(canvas);}}
}

最后MainPage.xmal.cs只需做少量修改就可以了

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.UI;
// https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x804 上介绍了“空白页”项模板namespace C_13_1_7
{/// <summary>/// 可用于自身或导航至 Frame 内部的空白页。/// </summary>public sealed partial class MainPage : Page{public MainPage(){this.InitializeComponent();}private void drawingCavas_Tapped(object sender, TappedRoutedEventArgs e){Point mouseLocation = e.GetPosition(this.draw);Square mySquare = new Square(100);mySquare.SetLocation((int)mouseLocation.X, (int)mouseLocation.Y);mySquare.Draw(draw);mySquare.SetColor(Colors.BlueViolet);}private void draw_RightTapped(object sender, RightTappedRoutedEventArgs e){Point mouseLocation = e.GetPosition(this.draw);Circle myCircle = new Circle(100);myCircle.SetLocation((int)mouseLocation.X, (int)mouseLocation.Y);myCircle.Draw(draw);myCircle.SetColor(Colors.HotPink);}}
}

visual C#(十三)创建接口和定义抽象类相关推荐

  1. 第十三章、创建接口和定义抽象类

    接口不包含任何代码或数据:它只规定了从接口继承的类必须提供哪些方法和属性.使用接口,方法的名称/签名可以和方法的具体实现完全隔绝. 抽象类在许多方面都和接口相似,只是它们可以包含代码和数据.然而,可以 ...

  2. Java声明定义抽象类_接口_继承_实现

    文章目录 声明定义抽象类 声明定义接口 派生类.抽象类.接口的继承要点 声明定义抽象类 public abstract class CRMSystem {public abstract Client ...

  3. Day16-01 P71 面向对象12什么是多态 P72 对象13:instanceof和类型转换 P73 static关键字详解 P74 抽象类 P75 接口的定义与实现 P76N种内部类

    Day16-01 P71 面向对象12:什么是多态 //父类Person package com.oop.demo06;public class Person {public void run(){S ...

  4. Java继承与多态(抽象类和接口练习)定义抽象类Bank,它包含oneyearRate(一年定期利率)oneyearNationalbebt(一年国债利率)和CurrentDepositRate(按年

    定义抽象类Bank,它包含oneyearRate(一年定期利率)oneyearNationalbebt(一年国债利率)和CurrentDepositRate(按年计算的活期利率)三个利息率常数(分别为 ...

  5. 设计一个接口, 并设计一个实现类实现该接口,演示它们的使用。具体,创建一个名称为Person的接口,在接口中定义两个方法sayHello()和sayBye()。

    设计一个接口, 并设计一个实现类实现该接口,演示它们的使用. 具体,创建一个名称为Person的接口,在接口中定义两个方法sayHello()和sayBye().然后,创建两个实现了Person接口的 ...

  6. jdk1.8.0_45源码解读——Map接口和AbstractMap抽象类的实现

    jdk1.8.0_45源码解读--Map接口和AbstractMap抽象类的实现 一. Map架构 如上图: (01) Map 是映射接口,Map中存储的内容是键值对(key-value). (02) ...

  7. 接口中定义的成员变量是( )。_抽象与接口

    原文链接:抽象与接口 抽象类 抽象类的定义 Java可以创建一种类专门用来当作父类,这种类称为"抽象类".抽象类的作用有点类似"模板",其目的是要设计者依据它的 ...

  8. 无法创建接口的实例_什么是接口?

    接口 接口概述 接口,是java语言中一种类型,是方法的集合,如果说 类的内部封装了成员变量,构造 方法,和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法,默认方法和静态方法 接口是对功能的 ...

  9. 接口可以继承抽象类吗_Python接口类的多继承以及抽象类的单继承

    一.接口类(面向对象开发的思想和规范)的多继承 需求: 定义一个tiger类:会走,会游 定义一个hawk类:会走,会飞 定义一个swan类:会走,会游,会飞 (a)如果像以下代码的方式,则不能够满足 ...

最新文章

  1. 还在用JDK6的同学,来看看JDK13新特性详解吧
  2. 前端笔记(3)css,选择器,文字文本属性,外观属性
  3. Blazor将.NET带回到浏览器
  4. Android动态替换dex,Android DexClassLoader动态加载与插件化开发
  5. 特征值与特征向量_矩阵的特征值和特征向量
  6. python表白程序-如何用Python代码向心爱的姑娘花式表白?
  7. Linux查找文件内容
  8. 自定义Toolbar的一些小技巧
  9. 介绍两种常见软件开发模式:“敏捷”和“瀑布”
  10. Mac电脑必备音乐下载工具-洛雪音乐助手lx-music-desktop v1.1.1正式版
  11. 555定时器原理及应用(报告)
  12. Euclid‘s Game(博弈)
  13. 云服务器搭建Git环境
  14. 如何获取某个月有多少天
  15. 微信公众平台-服务号:网页授权域名 设置
  16. 用vue2写的开发者在线简历导出
  17. 论文阅读:Channel Augmented Joint Learning for Visible-Infrared Recognition
  18. Doom-Emacs安装和基本使用方法
  19. 谷歌翻译下载-免费谷歌翻译软件下载
  20. 习SQL语句之SQL语句大全

热门文章

  1. 每个人心中都有一座“酋长岩”?
  2. 清明佳节将至,浅谈学习心得
  3. SoC设计内容和SoC设计流程总结
  4. 最有效的大脑休息方法
  5. MySQL 批量修改表名
  6. 通信笔记——数字基带传输系统
  7. C语言3进制加法口诀表代码完整版正确版本
  8. 关于AHT20温湿度传感器的学习以及使用altium designe绘制AHT20数据采集原理电路的过程
  9. 蓝桥杯--常用EXCEL技巧总结
  10. PCL 点读机,哪里不会查哪里!