任务一 类是什么
一、类(Class)是什么?
在面向对象编程中:
- 如果说对象是一个实体,那么类就是对象的定义。
- 类定义了对象特征和行为。
- 类是一个抽象的概念,它描述了具有相同属性和方法的对象的集合。
- 类是一种数据类型。
在生活中:
- 类是事物的抽象。比如:人类、鸟类都是事物的抽象。
- 类用来描述一类事物的共性。也就是事物的特征(属性)和行为(方法)
二、定义类的语法
一个典型的类通常由以下四种成员组成:
- 字段(Field):定义类的特征
- 属性(Property):封装字段
- 方法(Method):定义类的行为
- 构造函数(Constructor):初始化类
成员共同定义了类的特征和行为。不同类型的成员在面向对象编程中承担不同的角色,这些成员共同构建了类的结构和功能,使其具有自己的特性和行为。
using System;
// 定义顶级类
[类访问修饰符] [类修饰符] class 类名 [: 基类] [, 接口列表]
{
字段访问修饰符 数据类型 字段名称;//字段
//属性成员
//方法成员
//构造函数
}
class Program
{
// 定义嵌套类
[类访问修饰符] [类修饰符] class 类名 [: 基类] [, 接口列表]
{
字段访问修饰符 数据类型 字段名称;//字段
//属性成员
//方法成员
//构造函数
}
static void Main(string[] args)
{
}
}
类名
- 帕斯卡命名法:类名通常使用帕斯卡命名法:每个单词的首字母大写,其他小写。例如GetInfo等
- 语义化:类名应该具有描述性,以便在不查看类的定义的情况下也可以理解它的用途,避免使用单个字符或不具有描述性的类名。
- 名词化:如果类是具体的类型,类名通常使用名词,反映实际世界中的对象。的首字母大写。
三、对象是什么
对象(Object):根据类创建的具体实例。它包含了类定义的属性和方法的具体值。比如:
- 小花是一只具体的猫对象。
- 张三是一个具体的人对象。
- 李四也是一个具体的人对象。
四、创建对象的语法
using System;
// 定义类的位置
[类访问修饰符] [类修饰符] class 类名 [: 基类] [, 接口列表]
{
// 定义类 见上
}
class Program
{
static void Main(string[] args)
{
// 使用类的位置
// 创建类的示例对象的语法
类名 对象实例名称 = new 类的同名构造函数名();
// 字段的读操作
对象实例名称.字段名;
// 字段的写操作
对象实例名称.字段名 = 新值;
}
}
五、类成员的顺序
方法放在构造函数后面,是行业通用的代码组织习惯,而非语法强制要求:
// 语法上完全合法(方法在构造函数前)
public class Student
{
// 字段
private string _name;
// 属性
public string Name { get; set; }
// 方法(放在构造函数前)
public void ShowInfo()
{
Console.WriteLine(Name);
}
// 构造函数(在方法后)
public Student(string name)
{
Name = name;
}
}
上面的代码能正常编译运行,说明“方法位置”不影响功能——但行业里几乎都把方法放在构造函数后面,核心是为了可读性。
1.为什么习惯把方法放在构造函数后面?
类成员的排版遵循“先静态/固定的,后动态/功能的”原则,就像写文章“先介绍基础信息,再讲具体功能”:
| 成员类型 | 位置 | 原因(通俗解释) |
|---|---|---|
| 字段 | 最前 | 类的“底层数据”,所有成员都依赖它,先定义再使用 |
| 属性 | 字段后 | 对字段的封装,紧挨着字段,便于关联查看 |
| 构造函数 | 属性后 | 类的“初始化逻辑”,在字段/属性定义完成后,才能初始化它们 |
| 方法 | 构造函数后 | 类的“核心功能”,是初始化完成后才会执行的逻辑,放在最后更符合“先准备、后使用”的思维 |
举个生活例子:
定义“手机类”时,逻辑顺序是:
- 先有“品牌、型号”这些字段(手机的基础数据);
- 再有“获取/设置品牌”的属性(操作数据的接口);
- 然后有“开机初始化”的构造函数(拿到手机先开机);
- 最后有“打电话、发消息”的方法(开机后才能用的功能)。
如果把方法插在字段和构造函数之间,就像“还没准备好手机硬件,先讲怎么打电话”,逻辑上不连贯。
2.语法层面的“使用顺序”要求
虽然方法位置不强制,但成员的“定义顺序”必须满足“先定义,后使用”:
public class Student
{
// 1. 先定义字段
private string _name;
// 2. 构造函数使用字段(合法,因为字段已定义)
public Student(string name)
{
_name = name; // ✅ 字段已定义
}
// 3. 方法使用字段(合法,字段已定义)
public void ShowInfo()
{
Console.WriteLine(_name); // ✅
}
}
如果反过来,在构造函数/方法里使用未定义的字段,才会报错(但方法和构造函数的相对位置不影响):
public class Student
{
public Student(string name)
{
_name = name; // ❌ 报错:_name尚未定义
}
private string _name; // 字段定义在构造函数后,使用时未定义
}
3.类成员排版顺序
为了代码统一、易读,推荐固定顺序(所有.NET项目基本遵循):
- 静态字段 → 实例字段(先私有,后公共);
- 静态属性 → 实例属性;
- 构造函数(无参 → 有参);
- 静态方法 → 实例方法;
- 事件/索引器(进阶内容)。
示例(标准排版):
public class Student
{
// 1. 字段
private string _name;
private int _age;
// 2. 属性
public string Name { get; set; }
public int Age { get; set; }
// 3. 构造函数
public Student() { } // 无参
public Student(string name, int age) // 有参
{
Name = name;
Age = age;
}
// 4. 方法
public void ShowInfo()
{
Console.WriteLine($"{Name},{Age}岁");
}
public void Study()
{
Console.WriteLine($"{Name}正在学习");
}
}
总结
- 语法层面:方法放在构造函数前/后都合法,C#无强制要求;
- 规范层面:方法放构造函数后,符合“先定义数据→初始化→实现功能”的逻辑,提升可读性;
- 核心记忆:类成员排版的核心是“先基础(字段/属性),后初始化(构造函数),最后功能(方法)”,而非强制顺序;
- 新手重点:不用纠结“必须放哪”,先按这个顺序写,养成规范的代码习惯即可。
简单说:编译器不关心顺序,但人关心——好的顺序让你和同事能快速看懂代码。
五、什么时候嵌套类?
嵌套类的核心特性
嵌套类是定义在另一个类(外部类/包含类)内部的类,它的核心特性:
- 可以直接访问外部类的静态成员,但访问非静态成员需要外部类的实例;
- 按访问修饰符(public/private/protected/internal)控制可见性,默认是
private(仅外部类可访问); - 逻辑上属于外部类的“附属”,仅与外部类强关联,不适合被其他无关类使用。
场景1:为外部类服务的“辅助类”
当一个类的作用仅限于辅助外部类完成功能,且不会被其他类调用时,适合嵌套。(最常用) 👉 示例(动物叫声的辅助枚举嵌套在Animal类中):
using System;
public class Animal
{
// 嵌套枚举:仅Animal类使用的叫声类型,外部无需访问
private enum SoundType
{
Bark, // 狗叫
Meow, // 猫叫
Moo // 牛叫
}
// 移除属性的箭头语法(改用传统get/set写法,虽然原代码没箭头,但统一基础写法)
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
// 外部类方法使用嵌套枚举(移除switch表达式的=>,改用传统switch语句)
public void MakeSound(SoundType sound)
{
string soundStr;
// 传统switch语句替代switch表达式,无任何箭头语法
switch (sound)
{
case SoundType.Bark:
soundStr = "Woof";
break;
case SoundType.Meow:
soundStr = "Meow";
break;
case SoundType.Moo:
soundStr = "Moo";
break;
default:
soundStr = "未知叫声";
break;
}
Console.WriteLine($"{Name}发出了:{soundStr}");
}
// 新增:对外暴露调用入口(解决原Main中无法访问私有枚举的问题,让代码可运行)
public void MakeDogSound()
{
MakeSound(SoundType.Bark);
}
public void MakeCatSound()
{
MakeSound(SoundType.Meow);
}
public void MakeCowSound()
{
MakeSound(SoundType.Moo);
}
}
public class Program
{
public static void Main()
{
Animal dog = new Animal();
// 移除对象初始化器的箭头语法(改用传统赋值)
dog.Name = "狗";
// 调用新增的公开方法,间接使用私有枚举
dog.MakeDogSound();
Animal cat = new Animal();
cat.Name = "猫";
cat.MakeCatSound();
Animal cow = new Animal();
cow.Name = "牛";
cow.MakeCowSound();
}
}
场景2:封装“逻辑内聚”的子功能
当某个功能是外部类的“子模块”,且类名太通用(如Node/Item),嵌套可避免和其他类重名。
👉 示例(链表的节点类嵌套在链表类中):
public class SimpleLinkedList
{
// 嵌套类:仅链表使用的节点,外部无需知道节点的实现
private class Node
{
public int Value { get; set; }
public Node Next { get; set; }
}
private Node _head; // 链表头节点
// 外部类提供添加元素的方法,隐藏节点的操作
public void Add(int value)
{
Node newNode = new Node { Value = value };
if (_head == null)
{
_head = newNode;
return;
}
Node current = _head;
while (current.Next != null)
{
current = current.Next;
}
current.Next = newNode;
}
}
场景3:实现“仅外部类可用”的接口/功能
通过private嵌套类实现接口,外部无法直接实例化该类,仅外部类能提供访问入口,保证封装性。(控制访问范围)
👉 示例(外部类暴露功能,嵌套类实现具体逻辑):
// 定义一个简单接口
public interface ISoundMaker
{
void PlaySound();
}
public class Animal
{
public string Name { get; set; }
// 嵌套类:实现ISoundMaker,仅Animal类可访问
private class DogSound : ISoundMaker
{
public void PlaySound()
{
Console.WriteLine("Woof!");
}
}
// 外部类提供统一入口,隐藏具体实现
public void MakeDogSound()
{
ISoundMaker maker = new DogSound();
maker.PlaySound();
}
}
场景4:与外部类强关联的“数据容器”
当一个类仅用于存储外部类的临时数据/配置,且无独立存在的意义时,适合嵌套。 👉 示例(学生类中嵌套成绩容器):
public class Student
{
public string Name { get; set; }
// 嵌套类:仅存储学生的单科成绩,无独立意义
public class Score
{
public string Subject { get; set; }
public int Point { get; set; }
}
// 学生的成绩列表,使用嵌套类作为元素类型
public List<Score> Scores = new List<Score>();
}
public class Program
{
public static void Main()
{
Student stu = new Student { Name = "张三" };
// 嵌套类可通过“外部类.嵌套类”访问(如果是public)
stu.Scores.Add(new Student.Score { Subject = "数学", Point = 90 });
}
}
绝对不要用嵌套类的场景(避坑)
- 嵌套类需要被多个外部类使用(此时应独立定义);
- 嵌套类的逻辑复杂、代码量多(会导致外部类过于臃肿);
- 仅为了“代码归拢”而嵌套,无实际逻辑关联(如把无关的
Teacher类嵌套在Student类中)。
总结
- 嵌套类的核心适用场景:仅为外部类服务、逻辑强内聚、避免命名污染、控制访问范围;
- 设计原则:嵌套类应“轻量、内聚、无独立存在意义”,否则优先独立定义;
- 访问控制:默认
private(仅外部类可见),需对外暴露时设为public/internal。
示例
示例1:定义Animal类
要求:
- 字段:_name (存储动物名字)
- 行为:MakeSound() (定义动物的行为)
参考代码:
using System;
// 将Animal类设为public,且与Program同级(或设为static,适配静态Main方法)
public class Animal
{
// 私有字段 + 公共属性(原代码写法没问题,保留)
string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
// 重载1:无参数的MakeSound(基础版,通用叫声提示)
public void MakeSound()
{
Console.WriteLine($"{Name}发出了叫声~");
}
// 重载2:带字符串参数的MakeSound(指定具体叫声)
public void MakeSound(string sound)
{
Console.WriteLine($"{Name}发出了「{sound}」的声音");
}
// 重载3:带字符串+次数参数的MakeSound(参数个数不同,强化重载概念)
public void MakeSound(string sound, int times)
{
for (int i = 0; i < times; i++)
{
Console.WriteLine($"{Name}第{i+1}次叫:{sound}");
}
}
}
public class Program
{
public static void Main()
{
// 实例化猫
Animal cat = new Animal();
cat.Name = "猫";
// 调用无参的MakeSound
cat.MakeSound();
// 调用带1个参数的MakeSound(传入猫的叫声)
cat.MakeSound("Meow");
Console.WriteLine("-----分割线-----");
// 实例化狗
Animal dog = new Animal();
dog.Name = "狗";
// 调用带2个参数的MakeSound(传入狗的叫声+叫的次数)
dog.MakeSound("Woof", 3);
}
}
示例2:定义Person 类
- 字段:
name、age、gender- 存储人的基本信息 -
构造函数:创建对象时初始化数据 -
属性:提供对字段的安全访问 - 方法:定义人的行为(自我介绍、过生日、判断是否可以开车)
参考代码:
using System;
// 定义一个人类
public class Person
{
// 字段(存储数据)
private string name; // 姓名
private int age; // 年龄
private string gender; // 性别
// 构造函数(创建对象时自动调用)
public Person(string name, int age, string gender)
{
this.name = name;
this.age = age;
this.gender = gender;
}
/*
// 属性(访问字段)
public string Name
{
get { return name; }
set { name = value; }
}
public int Age
{
get { return age; }
set { age = value; }
}
public string Gender
{
get { return gender; }
set { gender = value; }
}
*/
// 自我介绍方法
public void Introduce()
{
Console.WriteLine($"大家好,我叫{name},今年{age}岁,性别{gender}。");
}
// 过生日方法(年龄加1)
public void HaveBirthday()
{
age++;
Console.WriteLine($"祝{name}生日快乐!现在{age}岁了。");
}
// 判断是否可以开车(假设18岁可以开车)
public void CanDrive()
{
if (age >= 18)
{
Console.WriteLine($"{name}可以开车了。");
}
else
{
Console.WriteLine($"{name}还不能开车,还差{18 - age}年。");
}
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("=== Person类示例 ===\n");
// 创建Person对象
Person person1 = new Person("张三", 20, "男");
Person person2 = new Person("李四", 17, "女");
// 调用对象的方法
Console.WriteLine("=== person1的信息 ===");
person1.Introduce(); // 输出:大家好,我叫张三,今年20岁,性别男。
person1.CanDrive(); // 输出:张三可以开车了。
Console.WriteLine("\n=== person2的信息 ===");
person2.Introduce(); // 输出:大家好,我叫李四,今年17岁,性别女。
person2.CanDrive(); // 输出:李四还不能开车,还差1年。
Console.WriteLine("\n=== person2过生日 ===");
person2.HaveBirthday(); // 年龄变为18
person2.CanDrive(); // 现在可以开车了
// 使用属性访问和修改数据
Console.WriteLine("\n=== 使用属性修改数据 ===");
Console.WriteLine($"修改前姓名:{person1.Name}");
person1.Name = "张三丰";
Console.WriteLine($"修改后姓名:{person1.Name}");
Console.WriteLine("\n按任意键退出...");
Console.ReadKey();
}
}
这里有几个关键点:
- Person: 数据类型。
- p1:变量名,表示一个Person类型的对象。
new关键字:表示在内存中“新建一个对象”。Person():调用的是构造函数- 在 C# 中,构造函数的名字必须和类名相同。如果你没写构造函数,C# 会自动提供一个默认的无参构造函数。
示例3:定义“手机”类
特点:无构造函数,仅属性+方法
要求:
- 类的定义
- 属性(get/set):Brand、 Model、Price
- 方法: MakeCall()、 SendMessage()
- 对象实例化
using System;
// 1. 定义最简单的类:手机类(包含属性和方法)
public class Phone
{
// 【属性】描述手机的特征(品牌、型号、价格)
public string Brand { get; set; } // 品牌
public string Model { get; set; } // 型号
public double Price { get; set; } // 价格
// 【方法】描述手机的行为(打电话、发消息)
// 方法1:打电话
public void MakeCall(string phoneNumber)
{
Console.WriteLine($"{Brand} {Model} 正在拨打 {phoneNumber}");
}
// 方法2:发消息
public void SendMessage(string content)
{
Console.WriteLine($"{Brand} {Model} 发送消息:{content}");
}
}
public class Program
{
public static void Main()
{
// 2. 实例化对象:创建具体的手机实例(相当于“造一台具体的手机”)
Phone iphone = new Phone();
// 3. 给对象的属性赋值
iphone.Brand = "苹果";
iphone.Model = "iPhone 15";
iphone.Price = 5999;
// 4. 调用对象的方法
Console.WriteLine($"手机信息:{iphone.Brand} {iphone.Model},价格:{iphone.Price}元");
iphone.MakeCall("10086");
iphone.SendMessage("你好,这是测试消息~");
// 再创建一个华为手机对象,对比展示“类是模板,对象是具体实例”
Phone huawei = new Phone();
huawei.Brand = "华为";
huawei.Model = "Mate 70";
huawei.Price = 6999;
huawei.MakeCall("10000");
}
}
运行结果:
手机信息:苹果 iPhone 15,价格:5999元
苹果 iPhone 15 正在拨打 10086
苹果 iPhone 15 发送消息:你好,这是测试消息~
华为 Mate 70 正在拨打 10000
总结:
class Phone:用class关键字定义类,类名首字母大写(命名规范);- 属性
Brand/Model/Price:描述类的特征,get/set表示可读写; new Phone():实例化对象,类是“模板”,对象是“具体的东西”;- 调用方法:
对象名.方法名(),调用属性:对象名.属性名。
示例4:定义“学生”类
特点:含无参/有参构造函数
要求:
- 类的定义
- 属性(get/set):Brand、 Model、Price
- 构造函数(无参/有参)
- 方法带返回值: ShowInfo()、 GetGrade()
- 对象实例化
using System;
// 定义学生类(封装私有字段,带构造函数)
public class Student
{
// 【私有字段】封装数据(新手理解:藏起来的变量,只能通过属性访问)
private string _name;
private int _age;
private double _score;
// 【公共属性】访问私有字段(封装的基础写法)
public string Name
{
get { return _name; }
set { _name = value; } // value是赋值时的传入值
}
public int Age
{
get { return _age; }
set
{
// 简单校验:年龄不能小于0
if (value < 0)
_age = 0;
else
_age = value;
}
}
public double Score { get; set; } // 简化写法(自动属性)
// 【构造函数1】无参构造函数(默认存在,可省略)
public Student()
{
// 初始化默认值
_name = "未知姓名";
_age = 0;
Score = 0;
}
// 【构造函数2】有参构造函数(重载,初始化时直接赋值)
public Student(string name, int age, double score)
{
_name = name;
_age = age;
Score = score;
}
// 【方法】计算等级(带返回值的方法)
public string GetGrade()
{
if (Score >= 90) return "优秀";
else if (Score >= 80) return "良好";
else if (Score >= 60) return "及格";
else return "不及格";
}
// 【方法】展示学生信息
public void ShowInfo()
{
Console.WriteLine($"姓名:{_name},年龄:{_age},成绩:{Score},等级:{GetGrade()}");
}
}
public class Program
{
public static void Main()
{
// 1. 用无参构造函数创建对象,再赋值
Student stu1 = new Student();
stu1.Name = "李四";
stu1.Age = 18;
stu1.Score = 85;
stu1.ShowInfo();
// 2. 用有参构造函数创建对象(更便捷)
Student stu2 = new Student("张三", 17, 92);
stu2.ShowInfo();
// 测试年龄校验:赋值负数,会被修正为0
Student stu3 = new Student();
stu3.Name = "王五";
stu3.Age = -5; // 非法年龄
stu3.Score = 58;
stu3.ShowInfo();
}
}
运行结果:
姓名:李四,年龄:18,成绩:85,等级:良好
姓名:张三,年龄:17,成绩:92,等级:优秀
姓名:王五,年龄:0,成绩:58,等级:不及格
总结:
- 构造函数:与类名同名、无返回值,用于初始化对象;
- 有参构造函数:创建对象时直接赋值,比“先new再赋值”更便捷;
- 封装:私有字段
_name/_age,通过公共属性控制访问(如年龄校验); - 方法返回值:
GetGrade()返回字符串,演示带返回值的方法。
示例5:定义“计算器”类
特点:带静态成员的类(区分实例成员 vs 静态成员)
要求:
- 类的定义
- 静态属性
- 构造函数(无参/有参)
- 静态方法
- 无需实例化即可调用
using System;
// 计算器类(以静态成员为主,适合工具类)
public class Calculator
{
// 静态属性:记录计算次数(所有对象共享)
public static int CalculateCount { get; set; } = 0;
// 静态方法:加法
public static int Add(int a, int b)
{
CalculateCount++; // 每次计算,次数+1
return a + b;
}
// 静态方法:乘法
public static int Multiply(int a, int b)
{
CalculateCount++;
return a * b;
}
// 实例方法:对比(需要实例化才能调用)
public void ShowTip()
{
Console.WriteLine("这是计算器的实例方法~");
}
}
public class Program
{
public static void Main()
{
// 1. 调用静态方法:无需new,直接“类名.方法名”
int sum = Calculator.Add(5, 10);
Console.WriteLine($"5+10={sum}");
int product = Calculator.Multiply(6, 8);
Console.WriteLine($"6×8={product}");
// 2. 访问静态属性:所有调用共享同一个值
Console.WriteLine($"累计计算次数:{Calculator.CalculateCount}"); // 输出2
// 3. 调用实例方法:必须先实例化对象
Calculator calc = new Calculator();
calc.ShowTip();
}
}
运行结果:
5+10=15
6×8=48
累计计算次数:2
这是计算器的实例方法~
知识点:
- 静态成员:用
static修饰,属于“类本身”,所有实例共享,无需new即可调用; - 实例成员:非静态,属于“具体对象”,必须实例化后才能调用;
- 工具类(如计算器、数学工具)通常用静态方法,方便直接调用。
示例6:定义“动物”基类 + “狗/猫”子类
特点:简单的类继承(拓展,基础进阶)
要求:
- 类的继承
- 方法重写(
virtual/override)
using System;
// 基类:动物(父类)
public class Animal
{
public string Name { get; set; }
// 虚方法:可被子类重写
public virtual void MakeSound()
{
Console.WriteLine($"{Name}发出了叫声~");
}
}
// 子类:狗(继承自Animal)
public class Dog : Animal
{
// 重写父类的方法
public override void MakeSound()
{
Console.WriteLine($"{Name}汪汪叫~");
}
// 子类独有方法
public void Run()
{
Console.WriteLine($"{Name}在跑~");
}
}
// 子类:猫
public class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine($"{Name}喵喵叫~");
}
}
public class Program
{
public static void Main()
{
Dog dog = new Dog();
dog.Name = "旺财";
dog.MakeSound(); // 调用重写后的方法
dog.Run(); // 子类独有方法
Cat cat = new Cat();
cat.Name = "咪咪";
cat.MakeSound();
}
}
运行结果:
旺财汪汪叫~
旺财在跑~
咪咪喵喵叫~
课堂讲解重点:
- 继承:用
:表示子类继承父类,子类可复用父类的属性/方法; - 方法重写:父类用
virtual,子类用override,实现“同行为不同表现”; - 子类可新增自己的方法(如
Run()),体现“拓展性”。
总结
- 基础类定义核心:属性(特征)+ 方法(行为),类是现实事物的抽象,对象是具体实例;
- 构造函数用于初始化对象,有参构造函数让赋值更便捷,封装(私有字段+公共属性)提升代码健壮性;
- 静态成员适合工具类,继承体现“复用+拓展”,是面向对象的核心基础;
- 示例按“基础→进阶”排序,可根据学生接受程度,先讲前3个核心示例,继承作为拓展内容。
这些示例覆盖了新手学习类定义的所有核心考点,场景贴近生活,代码注释清晰,适合课堂逐行讲解,也可让学生动手仿写(比如改写成“汽车类”“图书类”),强化理解。
挑毛病
using System;
public class Program
{
class Animal
{
string _name;
public string Name
{
get{ return _name;}
set{ _name = value;}
}
public void MakeSound(Name)
{
Console.WriteLine($"{Name}发出声音");
}
}
public static void Main()
{
Animal a1 = new Animal();
a1.Name = "猫";
a1.MakeSound();
}
}
核心问题
- 语法错误:MakeSound(Name) 方法定义时参数名不合法(不能直接用属性名Name当参数名,且缺少参数类型),且调用时无参但定义时要求传参,参数不匹配;
- 逻辑缺失:未体现动物叫声的差异化,未用到方法重载核心知识点;
- 访问修饰符问题:Animal类定义在Program内部,Main方法是静态的,直接实例化会有访问限制(新手易踩坑)。
修改后
using System;
// 修复:将Animal类设为public,且与Program同级(或设为static,适配静态Main方法)
public class Animal
{
// 私有字段 + 公共属性(原代码写法没问题,保留)
string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
// 重载1:无参数的MakeSound(基础版,通用叫声提示)
public void MakeSound()
{
Console.WriteLine($"{Name}发出了叫声~");
}
// 重载2:带字符串参数的MakeSound(指定具体叫声)
public void MakeSound(string sound)
{
Console.WriteLine($"{Name}发出了「{sound}」的声音");
}
// 重载3:带字符串+次数参数的MakeSound(参数个数不同,强化重载概念)
public void MakeSound(string sound, int times)
{
for (int i = 0; i < times; i++)
{
Console.WriteLine($"{Name}第{i+1}次叫:{sound}");
}
}
}
public class Program
{
public static void Main()
{
// 实例化猫
Animal cat = new Animal();
cat.Name = "猫";
// 调用无参的MakeSound
cat.MakeSound();
// 调用带1个参数的MakeSound(传入猫的叫声)
cat.MakeSound("Meow");
Console.WriteLine("-----分割线-----");
// 实例化狗
Animal dog = new Animal();
dog.Name = "狗";
// 调用带2个参数的MakeSound(传入狗的叫声+叫的次数)
dog.MakeSound("Woof", 3);
}
}
运行结果
猫发出了叫声~
猫发出了「Meow」的声音
-----分割线-----
狗第1次叫:Woof
狗第2次叫:Woof
狗第3次叫:Woof