3.1类 字段 方法
现实中的实物抽象为类
类最基本的要素是
字段:变量
方法:函数
构造方法(constructor)
构造方法的主要作用是完成对象的初始化工作
(1)构造方法的方法名与类名相同
(2)构造方法没有返回类型,也不能写void
默认(default)构造方法
如果用户没有定义任何构造方法
系统会默认一个构造方法
对象的创建
构造方法不能显示地直接调用,而是用new来调用
对象的使用
析构方法
由于C#自动进行对象的释放,所以用户一般不定义析构方法
方法的重载
方法的签名:方法名及参数个数及类型构成(参数名不算)
使用this
this指这个对象本身,常用于:
(1)访问这个对象的字段及方法
(2)区分字段与局部变量
(3)用于构造方法调用另一个构造方法,注意其位置
应用示例:银行系统
系统中有几类对象?Account,Bank,ATM
每个类中有什么字段、什么方法?
可在VS中,添加“类关系图”
//-------------------------------使用这些类-------------------------
public class BankDemo {
public static void Main(string [] args)
{
Bank bank = new Bank();
bank.OpenAccount("2222","2222", 20);
bank.OpenAccount("3333","3333", 50);
ATM atm = new ATM(bank);
for( int i=0; i<5; i++)
{
atm.Transaction();
}
}
}
//--------------账号类--------------------
public class Account {
double money; //decimal money;
string id;
string pwd;
//string name;
public Account( string id, string pwd,double money )
{
//if( money < 0 ) throw newException("....");
this.id = id;
this.pwd = pwd;
this.money = money;
}
public double getMoney()
{
return money;
}
public void setMoney(double val)
{
this.money = val;
}
public string getId()
{
return id;
}
public void setId(string id)
{
this.id = id;
}
public string getpwd()
{
return pwd;
}
public void setPwd(string pwd)
{
this.pwd = pwd;
}
public bool SaveMoney( double money)
{
if(money < 0 ) return false; //卫语句
this.money += money;
return true;
}
public bool WithdrawMoney( double money)
{
if( this.money >= money )
{
this.money -= money;
return true;
}
return false;
}
public bool IsMatch( string id, string pwd)
{
return id==this.id &&pwd==this.pwd;
}
}
//--------------------银行类----------------
using System;
using System.Collections;
using System.Collections.Generic;
public class Bank {
List<Account> accounts = newList<Account>();
public Account OpenAccount(string id,string pwd,
double money)
{
Account account = new Account(id, pwd,money);
accounts.Add( account );
return account;
}
public bool CloseAccount( Account account)
{
int idx = accounts.IndexOf(account);
if( idx<0 )return false;
accounts.Remove(account);
return true;
}
public Account FindAccount(string id,string pwd)
{
foreach(Account account in accounts)
{
if( account.IsMatch(id, pwd))
{
return account;
}
}
return null;
}
}
//---------------ATM---------
using System;
using System.Collections;
public class ATM {
Bank bank;
public ATM( Bank bank)
{
this.bank = bank;
}
public void Transaction()
{
Show("please insert your card");
string id = GetInput();
Show("please enter yourpassword");
string pwd = GetInput();
Account account = bank.FindAccount(id,pwd);
if( account == null)
{
Show("card invalid or password notcorrent");
return;
}
Show("1: display; 2: save; 3:withdraw");
string op = GetInput();
if (op == "1")
{
Show( "balance: " +account.getMoney() );
}
else if( op=="2")
{
Show("save money");
string smoney = GetInput();
double money = double.Parse(smoney);
bool ok = account.SaveMoney(money);
if( ok ) Show("ok");
else Show("eeer");
Show("balance: " +account.getMoney());
}
else if( op=="3" )
{
Show("withdraw money");
string smoney = GetInput();
double money = double.Parse(smoney);
bool ok = account.WithdrawMoney(money);
if( ok ) Show("ok");
else Show("eeer");
Show("balance: " +account.getMoney());
}
}
public void Show(string msg)
{
Console.WriteLine(msg);
}
public string GetInput()
{
return Console.ReadLine();// 输入字符
3.2属性 索引
使用属性、索引
使用属性button1.Text
使用属性 string s=”abcd”
使用索引 string s=”abcd”
属性(property)的书写
private string_name;
public string Name
{
get{
return_name;
}
set
{
_name=value;
}
}
在C#3.0以上版中可简写为
public string Name{set ;get;}
对属性进行访问
Person p=new Person();
p.Name=”Li ming”;
Console.WriteLine(p.Name);
编译器产生的方法是:
void set_Name(string value);
string get_Name();
属性与字段的比较
由于属性实际上是方法
所以属性可以具有优点
可以只读或只写:只有get或set
可以进行有效性检查:if…
可以使计算得到的数据
可以定义抽象属性
索引器(Indexer)
修饰符类型名 this[参数列表]
{
set{};get{};
}
使用索引
对象名参数[]
3.3类的继承
继承
子类父类
C#中采用单继承
所有的类都是通过直接或间接地继承
object即(System.Object)得到的。
子类自动地从父类那里继承所有的
字段、方法、属性索引器等成员作为自己的成员。
除了继承父类的成员外,子类还可以
添加新的成员
隐藏或修改父类的成员
方法的继承、添加
方法的继承(自动)
方法的添加(多定义一些方法)
与父类同名的方法
一是定义同名、但参数列表与父类不同的方法,这成为对父类方法的重载
二是定义同名且参数列表也与父类相同的方法,这称为新增加一种方法,用new表示
三是定义同名且参数列表也与父类相同的方法,而且父类的方法用来abstract或virtual进行修饰,子类的同名方法用了override进行了修饰,这称为虚方法的覆盖(Overriding)
使用base
父类与子类的转换
as运算符
如果不能转换,则值为null
Student s3 =p1 as student;
与强制类型转换的差别
as只能针对引用型变量
如果不能转换,as运算不会引起异常,只是值为null
is运算符
if(p is Person)
判断一个对象是不能某个类(及其子类)的实例
3.4修饰符
访问控制符
static
static的字段、方法、属性是属于整个类的
static方法中,不能访问实例变量
调用static方法时,直接用类名访问
static变量可以用来表示“全局变量”
在C#2.0,类名也可以用static来修饰
static构造方法
static构造方法只会调用一次,但其调用时间是不确定的
const及readonly
const相当于静态常量
readonly相当于不可改变量,只能赋一次值
如String.Empty
在构造方法中赋值,或者在声明时就赋值
const只能用于基本类型及string
readonly只能修饰字段,而const还可以修饰局部变量
sealed及abstract
sealed类,不可继承(有利于编译优化)
如String Console Math Convert Graphics Font
abstract类,不可实例化(new)
如Array,RandomNumberGenerator
abstract的方法体,不用{},用
abstract 类型 方法名(参数列表)
abstract 类型 属性名{get;set;}
3.5接口(interface)
接口实际上是一个约定
如:ICloneable。IComparable
接口是抽象成员的集合
ICloneable含有方法clone()
IComparable含有方法compare()
接口是一个引用类型,比抽象类更抽象
作用
帮组实现多重继承
实现不相关类的相同行为
不需要考虑这些类之间的层次关系
通过接口可以了解对象的交互界面,而不需了解对象所对应的类
public sealed class String:IComparable,ICloneable,IConvertible,IEnumerable
定义一个接口
实现接口
显示接口成员实现
在实现多个接口,如果不同的接口有同名的方法
为了消除奇异,需要在方法名前写接口名
调用时,只能用接口调用
总之
接口是一种约定
接口中有多个抽象方法
接口能实现多继承
面向接口进行编程
using System;
interface Runner
{
void run();
}
interface Swimmer
{
void swim();
}
abstract class Animal
{
abstract public void eat();
}
class Person : Animal , Runner, Swimmer
{
public void run()
{
Console.WriteLine("run");
}
public void swim()
{
Console.WriteLine("swim");
}
public override void eat()
{
Console.WriteLine("eat");
}
public void speak()
{
Console.WriteLine("speak");
}
}
class TestInterface
{
static void m1(Runner r)
{
r.run();
}
static void m2(Swimmer s)
{
s.swim();
}
static void m3(Animal a)
{
a.eat();
}
static void m4(Person p)
{
p.speak();
}
public static void Main(string [] args)
{
Person p = new Person();
m1(p);
m2(p);
m3(p);
m4(p);
Runner a = new Person();
a.run();
}
}
using System;
class InterfaceExplicitImpl
{
static void Main()
{
FileViewer f = new FileViewer();
f.Test();
( (IWindow) f ).Close();
IWindow w = new FileViewer();
w.Close();
}
}
interface IWindow
{
void Close();
}
interface IFileHandler
{
void Close();
}
class FileViewer : IWindow, IFileHandler
{
void IWindow.Close ()
{
Console.WriteLine( "Window Closed" );
}
void IFileHandler.Close()
{
Console.WriteLine( "File Closed" );
}
public void Test()
{
( (IWindow) this ).Close();
}
}
结构struct
结构常用来表示比较简单的多个分量
如Point Color Size DateTime Int32
可以有方法、属性等其他成员
using System;
struct Point
{
public double x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public double R(){
return Math.Sqrt(x*x+y*y);
}
}
class Test
{
static void Main() {
Point[] points = new Point[100];
for (int i = 0; i < 100; i++)
points[i] = new Point(i, i*i);
}
定义struct要注意
struct是值类型
实例化时,使用new,但与引用型变量的内存是不同的
值类型变量在赋值是,实行的是字段复制。
结构不能包含无参数构造方法
每个字段在定义时,不能给初始值
构造方法中,必须对每个字段进行赋值
struct是sealed的,不能被继承。
枚举
枚举实际上是有意义的整数
using System;
enum LightColor
{
Red,
Yellow,
Green
}
class TrafficLight
{
public static void WhatInfo(LightColor color) {
switch(color) {
case LightColor.Red:
Console.WriteLine( "Stop!" );
break;
case LightColor.Yellow:
Console.WriteLine( "Warning!" );
break;
case LightColor.Green:
Console.WriteLine( "Go!" );
break;
default:
break;
}
}
}
class Test
{
static void Main()
{
LightColor c = LightColor.Red;
Console.WriteLine( c.ToString() );
TrafficLight.WhatInfo( c );
}
}