【C#学习笔记】二、面向对象编程
2.1 抽象类与接口
1)概念
抽象类是特殊的类,只是不能被实例化;除此以外,具有类的其他特性;重要的是抽象类可以包括抽象方法,这是普通类所不能的。抽象方法只能声明于抽象类中,且不包含任何实现,派生类必须覆盖它们。另外,抽象类可以派生自一个抽象类,可以覆盖基类的抽象方法也可以不覆盖,如果不覆盖,则其派生类必须覆盖它们。接口是引用类型的,接口和抽象类实现了oop中的一个原则,把可变的与不可变的分离。抽象类和接口就是定义为不可变的,而把可变的作为子类去实现,接口和抽象类的相似之处有三点:
² 不能实例化;
² 包含未实现的方法声明;
² 派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法包括其他成员);
2)抽象类和接口的区别
² 类是对对象的抽象,可以把抽象类理解为把类当作对象,抽象成的类叫做抽象类.而接口只是一个行为规范或规定
² 接口基本上不具备继承的任何具体特点,它仅仅承诺了能够调用的方法
² 一个类一次可以实现若干个接口,但是只能扩展一个父类
² 接口除了可以包含方法之外,还可以包含属性、索引器、事件,而且这些成员都被定义为公有的。除此之外,不能包含任何其他的成员,例如:常量、域、构造函数、析构函数、静态成员。一个类可以直接继承多个接口,但只能直接继承一个类(包括抽象类)。
3)抽象类和接口的使用:
²
如果预计要创建组件的多个版本,则创建抽象类。抽象类提供简单的方法来控制组件版本。
²
如果创建的功能将在大范围的全异对象间使用,则使用接口。如果要设计小而简练的功能块,则使用接口。
²
如果要设计大的功能单元,则使用抽象类.如果要在组件的所有实现间提供通用的已实现功能,则使用抽象类。
²
抽象类主要用于关系密切的对象;而接口适合为不相关的类提供通用功能。
以下是我在网上看到的几个形象比喻,真的非常不错,呵呵:
1.飞机会飞,鸟会飞,他们都继承了同一个接口“飞”;但是F22属于飞机抽象类,鸽子属于鸟抽象类。
2. 就像铁门木门都是门(抽象类),你想要个门我给不了(不能实例化),但我可以给你个具体的铁门或木门(多态);
而且只能是门,你不能说它是窗(单继承);一个门可以有锁(接口)也可以有门铃(多实现)。 门(抽象类)定义了你是什么,接口(锁)规定了你能做什么(一个接口最好只能做一件事,你不能要求锁也能发出声音吧(接口污染))。
2.2 接口多态性
假定不使用基类提供EatFood()方法,而是把该方法放到接口IConsume上,Cow和Chicken类也支持这个接口(Cow和Chicken类必须提供EatFood()方法的执行代码),接着就可以使用下述代码访问该方法:
Cow myCow=new Cow();
Chicken myChicken=new Chicken();
IConsume consumeInterface;
consumeInterface=myConw;
consumeInterface.EatFood();
consumeInterface=myChicken;
consumeInterface.EatFood;
2.3 Window应用程序中的OOP(P180)
【例子1】
private void button1_Click(object
sender,System.EventArgs e)
{
((CButton)sender).Text=”Clicked!”;
Button
newButton=new Button();
newButton.Text=”New
Button!”;
newButton.Click+=new
EventHandler(newButton_Click);
Controls.Add(newButton);
}
private void newButton_Click(object
sender,System.EventArgs e)
{
((Button)sender).Text=”Clicked!”;
}
2.4 类的定义(P184)
C#中只能有一个基类,如果继承了一个抽象类,就必须实现所继承的所有抽象成员(除非派生类也是抽象的)。一个类可指定多个接口,如:
public class MyClass: MyBase, IMyInterface,
IMySecondInterface
{
}
修饰符 |
含义 |
无或internal |
类只能在当前项目中访问 |
public |
类可以在任何地方访问 |
abstract或internal abstract |
类只能在当前项目中访问,不能实例化,只能继承 |
public abstract |
类可以在任何地方访问,不能实例化,只能继承 |
sealed 或internal sealed |
类只能在当前项目中访问,不能派生,只能实例化 |
public sealed |
类可以在任何地方访问,不能派生,只能实例化 |
关键字abstract和sealed不能在接口中使用,因为这两个修饰符在接口定义中无意义(接口不包含执行代码,所以不能直接实例化,且必须是可以继承的)。接口可使用多个基接口,如:
public interface IMyInterface:
IMyBaseInterface, IMyBaseInterface2
{
}
2.5 构造函数执行顺序(P192)
public class MyBaseClass
{
public
MyBaseClass(){}
public
MyBaseClass(int i){}
}
public class MyDerivedClass: MyBaseClass
{
public
MyDerivedClass(){}
public
MyDerivedClass(int i){}
public MyDerivedClass(int
i, int j){}
}
MyDerivedClass myObj=new MyDerivedClass();
l
执行System.Object.Object()构造函数。
l
执行MyBaseClass.MyBaseClass()构造函数。
l
执行MyDerivedClass.MyDerivedClass()构造函数。
MyDerivedClass myObj=new MyDerivedClass(4);
l
执行System.Object.Object()构造函数。
l
执行MyBaseClass.MyBaseClass(int i)构造函数。
l
执行MyDerivedClass.MyDerivedClass(int i)构造函数。
MyDerivedClass myObj=new
MyDerivedClass(4,8);
l
执行System.Object.Object()构造函数。
l
执行MyBaseClass.MyBaseClass()构造函数。
l
执行MyDerivedClass.MyDerivedClass(int i,int j)构造函数。
若MyDerivedClass的定义做如下修改:
public class MyDerivedClass:MyBaseClass
{
….
public
MyDerivedClass(int i, int j) : base(i)
{}
}
则MyDerivedClass
myObj=new MyDerivedClass(4,8);其执行顺序如下:
l
执行System.Object.Object()构造函数。
l
执行MyBaseClass.MyBaseClass(i)构造函数。
l
执行MyDerivedClass.MyDerivedClass(int i,int j)构造函数。
若MyDerivedClass定义作如下修改:
public class MyDerivedClass:MyBaseClass
{
public
MyDerivedClass():this(5,6)
{}
public
MyDerivedClass(int i, int j) :base(i)
}
则MyDerivedClass
myObj=new MyDerivedClass();的执行顺序如下:
l
执行System.Object.Object()构造函数。
l
执行MyBaseClass.MyBaseClass(i)构造函数。
l
执行MyDerivedClass.MyDerivedClass(int i,int j)构造函数。
l
执行MyDerivedClass.MyDerivedClass()构造函数。
2.6 定义类成员(P209)
1)成员修饰符
public: 成员可以由任何代码范围
private: 成员只能由类中的代码访问(如果没有使用任何关键字,就默认使用这个关键字)
internal: 成员只能由定义它的项目(程序集)内部的代码访问
protected:
成员只能由类或派生类中的代码访问
2)方法修饰符
static:
只能通过类访问,不能通过对象实例化来访问
abstract: 方法必须在非抽象的派生类中重写(只用于抽象类中)
virtual:
方法可以重写
override: 方法重写了一个基类方法(如果方法被重写,就必须使用该关键字)
extern:
方法定义放在其它地方
【例子1】字段、属性与方法
public class MyClass
{
//使用readonly修饰,只能声明或构造函数中赋值
public
readonly string
Name;
private int
intVal;
//属性Val的访问器属性为protect所以不能直接在main中使用,但可在其派生类中使用
public
int Val
{
protected
get
{
return
intVal+1;
}
//不能同时设置两个访问器的权限
set
{
if (value >= 0 && value <= 10)
intVal = value;
else
//使用throw抛出异常
throw (new ArgumentOutOfRangeException("Val",value,"Val 必须是0~10"));
}
}
//重写了Object类的ToString方法,所以要用override修饰符
public
override string
ToString()
{
return
"Name:" + Name + "\nVal:" + Val;
}
//使用this关键字调用自身的MyClass(string newName)构造函数
private
MyClass(): this("Default
Name"){}
public
MyClass(string newName)
{
Name = newName;
intVal = 0;
}
}
public class MyClass2 : MyClass
{
Internal
int myVal;
//构造函数中使用base关键字调用基类的构造函数
public
MyClass2() : base("MyClass2"){}
//由于基类的Val属性的get访问器修饰符为protected,只能在类或派生类代码中访问
public
int Val
{
//派生类代码中可直接使用基类中Val属性的get和set访问器
get
{return base.Val;}
set
{base.Val = value;}
}
}
class Program
{
static void Main(string[] args)
{
MyClass2 obj2=new MyClass2();
Console.WriteLine(obj2.ToString());
obj2.myVal = 20;
Console.WriteLine("obj2.myVal={0}", obj2.myVal);
for
(int i = 0; i <= 11; i++)
{
obj2.Val = i;
Console.WriteLine("intVal={0}", obj2.Val);
}
Console.ReadKey();
}
}
【例子2】类中的静态字段和静态方法(Ch09Ex03)
class
Program
{
static void
Main(string[]
args)
{
//实例化时只能调用非静态函数及非静态字段,类调用时只能调用静态函数和静态字段
MyExternalClass
myClass = new MyExternalClass(2);
myClass.ShowIntVal();
MyExternalClass.strName
= "My Static Name";
MyExternalClass.ShowName();
Console.ReadKey();
}
}
public class MyExternalClass
{
private int
myIntVal;
public
static string
strName;
public
static void
ShowName()
{
Console.WriteLine(strName);
}
//使用构造函数来为私有变量myIntVal赋值
public MyExternalClass(int nVal)
{
myIntVal = nVal;
}
public
void ShowIntVal()
{
Console.WriteLine("MyIntVal={0}", myIntVal);
}
}
2.7 隐藏基类方法(P219)
class Program
{
static
void Main(string[] args)
{
DerivedClass1
C1 = new DerivedClass1();
DerivedClass2
C2 = new DerivedClass2();
C1.DoSomething1();
C2.DoSomething2();
BaseClass
C0;
C0 = C1;
C0.DoSomething1(); //执行基类的代码,输出 DoSomething1 In Base Class
C0=C2;
C0.DoSomething2(); //执行继承类的代码,输出DoSomething2 In DerivedClass2
}
}
class BaseClass
{
internal
void DoSomething1()
{
Console.WriteLine("DoSomething1 In Base Class");
}
//使用virtual修饰,表示方法可以被重写
virtual
internal void
DoSomething2()
{
Console.WriteLine("DoSomething2 In Base Class");
}
}
class DerivedClass1 : BaseClass
{
new
internal void
DoSomething1()
{
Console.WriteLine("DoSomething1 In DerivedClass1");
}
}
class DerivedClass2 : BaseClass
{
//override会重写基类代码
internal
override void
DoSomething2()
{
Console.WriteLine("DoSomething2 In DerivedClass2");
}
}
【C#学习笔记】二、面向对象编程的更多相关文章
- javascript 学习笔记之面向对象编程(二):继承&多态
~~接上篇~~上一篇实现了类的实现以及类成员变量和方法的定义,下面我们来了解下面向对象中两个最重要的特性:继承和多态. 继承 js中同样可以实现类的继承这一面向对象特性,继承父类中的所有成员(变量和属 ...
- python 学习笔记7 面向对象编程
一.概述 面向过程:根据业务逻辑从上到下写垒代码 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 面向对象:对函数进行分类和封装,让开发"更快更好更强..." ...
- C++ Primer 学习笔记_67_面向对象编程 --转换与继承、复制控制与继承
面向对象编程 --转换与继承.复制控制与继承 I.转换与继承 引言: 由于每一个派生类对象都包括一个基类部分,因此能够像使用基类对象一样在派生类对象上执行操作. 对于指针/引用,能够将派生类对象的指针 ...
- javascript 学习笔记之面向对象编程(一):类的实现
~~想是一回事,做是一回事,写出来又是一回事~~一直以来,从事C++更多的是VC++多一些,从面向过程到面向对象的转变,让我对OO的编程思想有些偏爱,将一个客观存在的规律抽象出来总是让人比较兴奋,通过 ...
- C++ Primer 学习笔记_69_面向对象编程 --继承情况下的类作用域
面向对象编程 --继承情况下的类作用域 引言: 在继承情况下,派生类的作用域嵌套在基类作用域中:假设不能在派生类作用域中确定名字,就在外围基类作用域中查找该名字的定义. 正是这样的类作用域的层次嵌套使 ...
- python学习笔记(七):面向对象编程、类
一.面向对象编程 面向对象--Object Oriented Programming,简称oop,是一种程序设计思想.在说面向对象之前,先说一下什么是编程范式,编程范式你按照什么方式来去编程,去实现一 ...
- 【c# 学习笔记】面向对象编程的应用
在平时的开发过程中,面向对象编程的应用肯定必不可少.但前面的内容只是单独介绍了类.面向对象思想和接口,那么我们怎么在平时工作中来应用他们来实现面向对象编程呢? 如果你想设计一个Dog类,有了类的概念后 ...
- JavaSE学习笔记05面向对象编程01
面向对象编程01 java的核心思想就是OOP 面向过程&面向对象 面向过程思想: 步骤清晰简单,第一步做什么,第二步做什么...... 面向过程适合处理一些较为简单的问题 面向对象思想: 物 ...
- JS 学习笔记 (七) 面向对象编程OOP
1.前言 创建对象有很多种方法,最常见的是字面量创建和new Object()创建.但是在需要创建多个相同结构的对象时,这两种方法就不太方便了. 如:创建多个学生信息的对象 let tom = { n ...
- Spark学习笔记11面向对象编程
面向对象编程 11.1 object类 11.1.1定义一个简单的类 11.1.2 field的getter与setter 定义类包含,定义类的field及方法.其格式如下 class Cla ...
随机推荐
- nosql学习一
1.NoSQL,泛指非关系型的数据库.随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难 ...
- C# Attribute
Attribute 是C#非常重要的一块内容,需要研究一下. Attribute 的简单使用:简而言之,就是可以自定义通用标志位,而不是在每个所需的类型中分别增加标志位. //class专用attr ...
- 尝试跑一跑Scut
前段时间都在用 IIS+WCF+Redis+MSSQL 的框架做服务器,前段时间看到了 Scut 的开源框架,整个架构还是蛮干净整洁的... 今天抓来跑一跑. 按照教程安装好所有的环境,版本是6.7. ...
- Mac中的快捷键
Mac中主要有四个修饰键,分别是Command,Control,Option和Shift,这四个键分别有自己的图案 基本快捷键 Command是Mac里最重要的修饰键,在大多数情况下相当于Window ...
- Xamarin for OSX – SetUp
正常情况联网会失败 按照安装顺序进行安装(mono framework->java sdk-> android sdk->xamarin studio->xamarin.and ...
- 【最短路】Vijos P1046 观光旅游
题目链接: https://vijos.org/p/1046 题目大意: 给n个点(n<=100),m条无向边(m<=10000),问这张图的最小环长度. (注意:无自环,同一个点对之间的 ...
- Ubuntu输入密码之后,桌面闪一下黑屏,然后又返回到输入密码界面。但是其他账户可以登入
1)原因:主目录下的.Xauthority文件拥有者变成了root,从而以用户登陆的时候无法都取.Xauthority文件 说明:Xauthority,是startx脚本记录文件.Xserver启动时 ...
- Purchase Document Open Interface(PDOI)
PO模块也有自己的接口表,多用于把其他业务系统在Oracle EBS系统生成采购订单记录. Table Name Description Type PO_HEADERS_INTERFACE This ...
- java异常面试常见题目
在Java核心知识的面试中,你总能碰到关于 处理Exception和Error的面试题.Exception处理是Java应用开发中一个非常重要的方面,也是编写强健而稳定的Java程序的关键,这自然使它 ...
- 初次使用cocoapods注意事项
在仅仅用cocoapods时可能会遇到各种各样的错误和问题 这里中总结下: 1.首先使用cocoapods有非常多优点,在github上非常多优秀的开源项目都用到了它;假设你不会使用它,那么非常多优秀 ...