C# -写个猫狗大战(面向对象的三大特征)
效果
程序分析
- 资源准备
- 需要事先找好两个普通png格式的图(卡通猫和卡通狗)
- 两个gif动图,用来显示初始的 “挑衅“
- 三个gif的动图;石头、剪刀、布
- 将图片的资源文件都先添加到项目中;项目右侧有个Resources.resx文件,点击出现左侧界面,选中添加资源,选则添加现有文件,从本地文件中选则下载的七个图。 添加完成后,项目中会自动生成一个文件夹里面都是刚添加的图片文件
-
需要 4个PictureBox 图片控件,并将其SizeMode属性都设置为 StretchImage
- 第一个用来设置猫主图
- 第二个用来设置狗主图
- 第三个用来设置猫出的招
- 第四个用来设置狗出的招
-
两边的动物属性,一边需要6个label控件,并将其Text 值改为对应的文案
-
还需要一个label,将期Text值清空,放在中间,最后用来提示是谁赢了,把字段设置大点
-
需要一个Button (开始)按钮,双击自动生成点击事件 双方出招,代码逻辑如下
- 猫和狗都重新出招
- 对比双方输赢,提示出来
-
两个Button (切换猫、切换狗)按钮,自动生成点击事件,用来切换动物,代码逻辑如下
- 重新初始化猫和狗相关属性**(注:此处动物的主图出可重新初始化,此处因没下太多资源图片,所以暂时做成了不切换)**
- 重新初始化猫和狗相关属性**(注:此处动物的主图出可重新初始化,此处因没下太多资源图片,所以暂时做成了不切换)**
-
定义两个全局变量狗和猫对象,用在 输出提示时,知道当前输赢的动物名称
-
定义一个动物类,让猫和狗都继承动物类,抽取公共方法,方便调用
直接上代码
窗体初始化
// 初始化页面动物相关属性
Dog dog = new Dog("阿奇", 12, "公");
Cat cat = new Cat("Tom猫", 8, "公");
private void Form1_Load(object sender, EventArgs e)
{
// 此处采用多态的形式(即,父类引用指向子类对象)
AnimalAttackInit(dog, 2);
AnimalAttackInit(cat, 1);
//初始化出招
this.pictureBox3.Image = Properties.Resources.mao1;
this.pictureBox3.SizeMode = PictureBoxSizeMode.StretchImage;
this.pictureBox4.Image = Properties.Resources.gou1;
this.pictureBox4.SizeMode = PictureBoxSizeMode.StretchImage;
}
private void AnimalAttackInit(Animal animal,int animalType)
{
Image im = animal.Attack();
if(animalType == 1)
{
// 猫
this.label2.Text = animal.name;
this.label3.Text = animal.age.ToString();
this.label5.Text = animal.sex;
this.pictureBox3.Image = im;
this.pictureBox3.SizeMode = PictureBoxSizeMode.StretchImage;
}
else
{
// 狗
this.label11.Text = animal.name;
this.label9.Text = animal.age.ToString();
this.label7.Text = animal.sex;
this.pictureBox4.Image = im;
this.pictureBox4.SizeMode=PictureBoxSizeMode.StretchImage;
}
}
点击开始按钮
- 猫和狗都重新出招
- 对比双方输赢,提示出来
private void button1_Click(object sender, EventArgs e)
{
// 猫
this.pictureBox3.Image = cat.Attack();
int catAttackNum= cat.attackNum;
// 狗
this.pictureBox4.Image = dog.Attack();
int dogAttackNum = dog.attackNum;
if(catAttackNum > dogAttackNum)
{
this.label13.Text = String.Format("{0}赢了!!!!!!",cat.name);
}else if(catAttackNum < dogAttackNum)
{
this.label13.Text = String.Format("{0}赢了!!!!!!", dog.name);
}
else
{
this.label13.Text = String.Format("{0}与{1}打平了!!!!!!", cat.name,dog.name);
}
}
点击换猫按钮,重新初始化猫相关属性
private void button2_Click(object sender, EventArgs e)
{
// 换猫, 定义一个随机名字的数组
string[] catName = { "Tom猫", "白猫", "黑猫", "灰猫" };
Random rand = new Random();
string name = catName[rand.Next(0, 4)];
int age = rand.Next(1, 20);
string sex = rand.Next(1, 3) == 1 ? "公" : "母";
cat = new Cat(name,age,sex);
// 猫
this.label2.Text = cat.name;
this.label3.Text = cat.age.ToString();
this.label5.Text = cat.sex;
}
点击换狗逻辑,初始化狗相关属性
private void button4_Click(object sender, EventArgs e)
{
// 换狗, 定义一个随机名字的数组
string[] dogName = { "天天", "阿奇", "小丽", "阿宝" };
Random rand = new Random();
string name = dogName[rand.Next(0, 4)];
int age = rand.Next(1, 20);
string sex = rand.Next(1, 3) == 1 ? "公" : "母";
dog = new Dog(name, age, sex);
// 猫
this.label11.Text = dog.name;
this.label9.Text = dog.age.ToString();
this.label7.Text = dog.sex;
}
Animal 类
internal class Animal
{
/// <summary>
///姓名
/// </summary>
public string name;
/// <summary>
/// 年龄
/// </summary>
public int age;
/// <summary>
/// 性别
/// </summary>
public string sex;
/// <summary>
/// 出招数
/// </summary>
public int attackNum;
public Animal()
{
}
public Animal(string name, int age, string sex)
{
this.name = name;
this.age = age;
this.sex = sex;
}
/// <summary>
/// 出招
/// virtual 关键字表示 该方法可以被子类重写,是否重写在于子类
/// </summary>
public virtual Image Attack()
{
Image shitou = Properties.Resources.shitou;
Image jiandao = Properties.Resources.jiandao;
Image bu = Properties.Resources.bu;
// 产生一个随机数,1 表示石头、2表示剪刀、3表示布
Random random = new Random();
this.attackNum = random.Next(1, 4);
Image result = null;
switch (attackNum)
{
case 1:
result = shitou;
break;
case 2:
result = jiandao;
break;
case 3:
result = bu;
break;
default:
break;
}
return result;
}
}
猫类
internal class Cat: Animal
{
public Cat(string name, int age, string sex)
{
this.name = name;
this.age = age;
this.sex = sex;
}
/// <summary>
/// 出招
/// override 关键字表示 重写父类的方法
/// </summary>
public override Image Attack()
{
// 自已的逻辑
Console.WriteLine("大猫丢出了小鱼,请接招...");
//调用父类出招
return base.Attack();
}
}
狗类
internal class Dog: Animal
{
// 属性都直接继承父类
public Dog(string name,int age,string sex)
{
this.name = name;
this.age = age;
this.sex = sex;
}
/// <summary>
/// 出招
/// override 关键字表示 重写父类的方法
/// </summary>
/// <returns></returns>
public override Image Attack()
{
// 自已的逻辑
Console.WriteLine("大狗丢出了骨头,请接招...");
//调用父类出招
return base.Attack();
}
}
面向对象三大特征
继承
- 继承表示一个类型派生于一个基类型,它拥有该基类型的所有成员字段和函数,其子类是对父类的扩展
- 使用继承而产生的类被称为派生类或子类,而被继承的类则称为基类或超类或父类
- 继承最大的好处是实现代码的高效重用
- 继承都是单一的,即:只能继承一个类
- 如下述案例中的Animal是父类,Dog和 Cat 都是其子类,Dog和Cat中不需要写任何属性,因为已经继承了父类中的 属性,继承后子类也会有相同的属性
- 继承后,子类可以重写父类的方法,子类在方法中可以实现自己特有的逻辑,如上述案例中的 Attack() 方法。子类增加了自己 “丢出了鱼 或 丢出了骨头” 的逻辑; 子类有需要就调用父类的方法 base.Attack(), 没有需要也可以不调用; 重写父类的方法步骤如下:
- 在父类需要被重写的方法前加上 virtual 关键字
- 在子类相同的方法前加上 override 关键字
封装
- 隐藏内部功能的具体实现,只保留和外部交流数据的接口,例如 电视机,用一个外壳把电路板和零件封装起来,只对外提供了几个按钮和遥控器。 使用的人不需要知道 其内部的电路是如何走的,只要会按按钮就行
- 程序中的实现就如上述案例中的 Animal 类的 Attack()方法一样,我们不需要知道 Attack() 方法的内部逻辑具体是如何写的,我们只需要知道,调用 Attack()方法能帮助我们做什么
多态
- 程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定。
- 多态常用于 子类重写了父类的方法,当我们有多个子类都需要调用某个共同的方法时。如上述案例中的 Animal 类的 Attack()方法, 猫和狗都需要调用
- 简单理解就是 父类的引用 指向了子类的对象
// 父类引用 指向子类对象
Animal animal=new Dog();
Animal animal2=new Cat();
animal.Attack(); // 这时调用的其实是 Dog中的 Attack() 方法
animal2.Attack(); // 这时调用的其实是 Cat中的 Attack() 方法
或 通过方法的形式(同样是父类引用指向子类对象)
private void testAttack(Animal animal)
{
// 只看这个方法,我们不知道进来的动物是狗还是猫,只有在程序运行的时候才知道
animal.Attack();
}
public static void Main()
{
Dog dog=new Dog();
Cat cat =new Cat();
testAttack(dog);
testAttack(cat);
}