目录
属性
定义
- 定义属性需要名字和类型
- 属性包含两个块 get块和set块
- 访问属性和访问字段一样,当取得属性的值的时候,就会调用属性中的get块,所以get块需要一个返回值,类型就是属性的类型;当我们去给属性设置值的时候,就会调用属性中的set块,我们可以在set块中通过value访问到我们设置的值。
public int MyIntProp
{
get{
get code
}
set{
set code
}
}
实例:
定义一个MyProperty类
namespace _016_属性的定义
{
class MyProperty
{
//定义属性,get和set块可以只有一个或者两个都有
public int MyIntProperty
{
get
{
Console.WriteLine("属性中的get块被调用");
return 100; //get块里需要一个返回值
}
set
{
Console.WriteLine("属性中的set块被调用");
Console.WriteLine("在set块中访问value的值:" + value);
}
}
}
}
调用
namespace _016_属性的定义
{
class Program
{
static void Main(string[] args)
{
MyProperty myProperty = new MyProperty();
myProperty.MyIntProperty = 200; //给属性设置值的时候,调用set块
Console.WriteLine(myProperty.MyIntProperty); //访问的时候,调用get块
Console.ReadKey();
}
}
}
通过属性来访问字段
我们习惯上把字段设置为私有的,这样外界就不能修改字段的值,之后我们可以通过定义属性来设置和取得字段中的值
private int age;
public int Age
{
get { return age; }
set{
//通过set方法在设置值之前做一些校验的工作
if (value>0)
{
age = value;
}
}
}
其他效果
- 设置属性的只读或者只写
private string name;
public string Name
{
get { return name; }
}
- 属性的访问修饰符
private string name;
public string Name //也可以叫做get set 方法
{
//如果在get或者set前面加上private,表示这个块只能在类内部调用,也就是这个属性只读/只写
get { return name; }
private set { name = value; }
}
- 自动实现的属性
//编译器会自动给我们提供一个字段/数据成员,不用我们定义
public string Sex { get; set; }
面向对象编程
定义
- 为了让编程更加清晰,把程序中的功能进行模块化划分,每个模块提供特定的功能,而且每个模块都是孤立的,这种模块化编程提供了非常大的多样性,大大增加了重用代码的机会。
- 面向对象编程也叫做OOP编程。简单来说面向对象编程就是结构化编程,对程序中的变量结构划分,让编程更清晰。
类
- 类创建的变量叫做对象。 类实际上是创建对象的模板,每个对象都包含数据,并提供了处理和访问数据的方法。
- 类定义了类的每个对象(称为实例)可以包含什么数据和功能。
类的定义和声明
类中的数据和函数称为类成员,包含:
-
数据成员:数据成员是包含类的数据–字段,常量和事件的成员。
-
函数成员:提供了操作类中数据的某些功能。(方法,属性,构造方法和终结器(析构方法),运算符和索引器)。
-
实例
定义一个Customer类
namespace _015_面向对象编程_类
{
class Customer //定义了一个新的类叫Customer类
{
//数据成员:包含2个字段
public string name;
public int age;
//函数成员:定义了一个方法
public void show()
{
Console.WriteLine("名字:" + name);
Console.WriteLine("年龄:" + age);
}
}
}
定义一个Vector3类
namespace _015_面向对象编程_类
{
class Vector3
{
//编程规范上,习惯把所有字段的访问修饰符设置为private,只可以在类内部访问,不可以通过对象访问
private float x,y,z;
//为字段提供set方法,来设置字段的值
public void setX(float x)
{
//如果我们直接在方法内部访问同名的变量的时候,优先访问形参
//使用this.表示访问的是类的字符或者方法
this.x = x;
}
public void setY(float y)
{
this.y = y;
}
public void setZ(float z)
{
this.z = z;
}
public float Length()
{
return (float)Math.Sqrt( Math.Pow(x, 2) + Math.Pow(y, 2) + Math.Pow(z, 2) );
}
}
}
调用
namespace _015_面向对象编程_类
{
class Program
{
static void Main(string[] args)
{
//要使用一个类的话,需要先引入它的命名空间
Customer customer1; //声明一个对象(变量),也称之为实例化
customer1 = new Customer(); //对对象进行初始化,需要用new + 类名
//使用类声明的对象中的变量,还有方法
customer1.name = "China";
customer1.age = 70;
customer1.show();
Vector3 v1 = new Vector3();
v1.setX(1);
v1.setY(1);
v1.setZ(1);
Console.WriteLine("\n" + v1.Length());
Console.ReadKey();
}
}
}
PS:实参和形参
方法中的参数分为实际参数和形式参数,实际参数被称为实参,是在调用方法时传递的参数;形式参数被称为形参,是在方法定义中所写的参数。
public int Add(int a,int b) //a 和 b 是形式参数
{
return a+b;
}
public void Print()
{
Add(3,4); //调用 Add 方法时传递的参数 3 和 4 即为实际参数。
}
构造函数
自行为类声明一个对象(变量),分为有参和无参
定义一个Vector3类
namespace _015_面向对象编程_类
{
class Vector3
{
private float x, y, z,length;
//构造函数,我们可以使用有参的或者无参的构造函数进行初始化,之后编译器不会为我们自动提供构造函数
public Vector3() //无参的构造函数
{
Console.WriteLine("Vector3类的构造函数被调用了");
}
public Vector3(float x, float y, float z) //有参的构造函数
{
this.x = x;
this.y = y;
this.z = z;
length = Length();
Console.WriteLine(length);
}
public float Length()
{
return (float)Math.Sqrt( Math.Pow(x, 2) + Math.Pow(y, 2) + Math.Pow(z, 2) );
}
}
}
调用
namespace _015_面向对象编程_类
{
class Program
{
static void Main(string[] args)
{
//构造函数
Vector3 v1 = new Vector3();
Vector3 v2 = new Vector3(1, 1, 1);
Console.ReadKey();
}
}
}
继承
实现继承
表示一个类型派生于一个基类型,它拥有该基类型所有的成员字段和函数。在实现继承中,派生类型采用基类的每个函数的实现代码,除非在派生类型的定义中指定重写某个函数的实现代码。在需要给现有的类型添加功能,或许多相关的类型共享一组重要的公共功能时,这种类型的继承非常有用。
class MyDerivedClass:MyBaseclass
{
functions and data members here
}
- 实例:
父类(基类) Enemy
namespace _017_继承
{
class Enemy
{
private float hp;
private float speed;
public float Hp
{
get { return hp; }
set { hp = value; }
}
public float Speed
{
get { return speed; }
set { speed = value; }
}
public void AI()
{
Console.WriteLine("这是Enemy类公有的AI方法");
}
}
}
子类(派生类)Boss
class Boss:Enemy
{
public void Attack()
{
Console.WriteLine("Boss正在进行攻击");
}
}
class Program
{
static void Main(string[] args)
{
//继承:父类里面所有的数据成员和函数成员都会继承到子类里面
Boss boss = new Boss();
boss.AI(); //继承父类的方法
boss.Attack();
//可以用父类声明对象,然后用子类构造(实例化),反之子类声明的对象不可以使用父类构造
Enemy enemy;
enemy = new Boss();
//enemy.Attack(); //此时的对象仍是父类类型,需要强制类型转换为子类类型
Boss boss1 = (Boss)enemy;
boss1.Attack();
Console.ReadKey();
}
}
接口继承
表示一个类型只继承了函数的签名,没有继承任何实现代码。在需要指定该类型具有某些可用的特性时,最好使用这种类型的继承。
虚方法
把一个基类函数声明为virtual,就可以在任何派生类中重写该函数:
class Enemy
{
public virtual void Move()
{
Console.WriteLine("这是Enemy类公有的Move方法");
}
}
在派生类中重写另外一个函数时,要使用override关键字显示声明:
class Boss:Enemy
{
public override void Move()
{
//base.Move();
Console.WriteLine("Boss正在移动");
}
}
}
class Program
{
static void Main(string[] args)
{
Boss boss = new Boss();
boss.Move(); //调用子类中重写的方法
Enemy enemy = new Enemy();
enemy.Move(); //调用父类中的虚方法
Console.ReadKey();
}
}
隐藏方法
如果签名相同的方法在基类和派生类中都进行了声明,但是该方法并没有分别声明virtual和override,这是派生类就会隐藏基类方法(可以使用new关键字进行声明)
一个方法的签名即为一个方法的定义,例如返回值、参数、方法名等。
class Enemy
{
public void AI()
{
Console.WriteLine("这是Enemy类公有的AI方法");
}
}
class Boss:Enemy
{
public new void AI()
{
Console.WriteLine("这是Boss的AI方法");
}
}
class Program
{
static void Main(string[] args)
{
Boss boss = new Boss();
boss.AI(); //This is AI from Boss!
Enemy boss = new Boss();
boss.AI(); //This is AI from Enemy!
Console.ReadKey();
}
}
隐藏和重写的区别:
隐藏:只是将父类中的方法给隐藏了,实际这个方法还存在。
重写:将原先父类中的方法完全重写了,原先的方法是不存的了。
使用子类构造的时候,重写访问的是子类的,隐藏访问的是父类的
Enemy enemy = new Enemy(); //都是父类的方法
enemy.Move();
enemy.AI();
Enemy enemy = new Boss();
enemy.Move(); //重写
enemy.AI(); //隐藏
一般不去使用隐藏方法,因为很容易引起方法调用的混乱。
this 和base关键字的作用
- this:可以访问当前类中定义的字段、属性和方法;另外当方法的参数跟字段重名的时候,使用this可以表明访问的是类中的字段
- base:可以调用父类中的公有方法和字段
有没有this和base关键字都可以访问,但是加上关键字后IDE-VS编译器会给出提示,把所有可以调用的字段和方法罗列出来方便选择
抽象类
C#允许把类和函数声明为abstract。抽象类不能实例化,但可以包含普通函数和抽象函数,抽象函数就是只有函数定义没有函数体,定义的时候需要使用关键字"abstract",派生类中必须重写基类所有的虚方法。
实例:
基类Bird:
abstract class Bird //当类中存在抽象方法的时候,这个类也要声明为抽象的
{
private float speed;
public void Eat()
{
}
public abstract void Fly(); //定义一个虚方法
}
派生类Crow:
class Crow:Bird //继承一个抽象类后,必须去实现抽象方法
{
public override void Fly() //关键字override重写
{
Console.WriteLine("乌鸦在飞");
}
}
class Program
{
static void Main(string[] args)
{
Bird bird = new Crow(); //我们可以通过抽象类去声明对象,但是不可以构造
bird.Fly();
Console.ReadKey();
}
}
密封类和密封方法
C#允许把类和方法声明为sealed。对于类,这表示不能继承该类;对于方法表示不能重写该方法。 当防止重写某些类导致代码混乱的时候可以使用。
sealed class BaseClass //密封类无法被继承
{
public sealed void Move() //密封方法不能被重写
{
}
}
派生类的构造函数
在子类中调用父类的默认构造函数(会先调用父类的,然后是子类的)。
无参的base()可以不写,因为默认会调用父类中的默认构造函数(调用有参的要写)
namespace _018_派生类的构造方法
{
class BaseClass
{
private int x;
public BaseClass() //无参构造函数
{
Console.WriteLine("Base Class无参构造函数");
}
public BaseClass(int x) //有参构造函数
{
this.x = x;
Console.WriteLine("x赋值完成");
}
}
}
class DerivedClass:BaseClass
{
private int y;
public DerivedClass() :base()//调用父类中无参的构造函数。如果我们没有在父类中创建,默认会调用父类中的无参构造函数
{
Console.WriteLine("这个是DerivedClass无参的构造函数");
}
public DerivedClass(int x,int y) : base(x) //调用父类中有参的构造函数
{
this.y = y;
Console.WriteLine("y赋值完成");
}
}
class Program
{
static void Main(string[] args)
{
DerivedClass derivedClass1 = new DerivedClass();
DerivedClass derivedClass2 = new DerivedClass(1,2);
Console.ReadKey();
}
}
访问修饰符
修饰符,用来修饰类型或者成员的关键字。修饰符可以指定方法的可见性。
其他修饰符
static可以修饰字段或者方法,修饰字段的时候,表示这个字段是静态的数据,叫做静态字段或者静态属性,修饰方法的时候,叫做静态方法,或者静态函数
使用static修饰的成员,只能通过类名访问。 当我们构造对象的时候,对象中只包含了普通的字段,不包含静态字段。
class DerivedClass:BaseClass
{
public static int z=1;
public static void TestMothod()
{
Console.WriteLine("静态方法");
}
}
class Program
{
static void Main(string[] args)
{
//只能通过类访问静态字段或方法
Console.WriteLine(DerivedClass.z);
DerivedClass.TestMothod();
Console.ReadKey();
}
}
定义和实现接口
定义一个接口在语法上跟定义一个抽象类完全相同,但不允许提供接口中任何成员的实现方法。注:
- 一般情况下,接口只能包含方法,属性,索引器和事件的声明。
- 接口不能有构造函数,也不能有字段,接口也不允许运算符的重载。
- 接口定义中不允许声明成员的修饰符,接口成员都是公有的。
定义接口
namespace _019_定义和实现接口
{
interface IFly //关键字interface,以I开头
{
//接口中不能实现函数
void MethodA(); //方法只能够有签名,没有结构体
void MethodB(); //默认都是公有的
}
}
实现接口
class Bird : IFly
{
//实现接口
public void MethodA(){
}
public void MethodB(){
}
}
Trick:
- 抽象类和接口都不能实例化(构造),两者的方法都只有函数定义,没有函数体
- 虚函数以重写之后的为准,就算调用基类的也是一样
- 构造函数声明的时候,构造函数里的东西就被调用了
- 在抽象类中,不一定所有的方法都是抽象方法,抽象类不需要实现
- 在赋值的时候,值类型和引用类型要区分清楚
Public abstract Animal //在抽象类中,不一定所有的方法都是抽象方法,抽象类不需要实现
{
public abstract void Eat();
public void Sleep()
{
}
}