面向对象(C#基础)
文章目录
类和对象
类和对象
C# 中的一切都与类和对象及其属性和方法相关联。例如:在现实生活中,汽车是一个物体。汽车有属性,比如重量和颜色,还有方法,比如驱动和刹车。
类类似于对象构造函数,或用于创建对象的"蓝图"。
创建一个类
要创建类,请使用class
关键字:
创建一个名为"Car
"且color
颜色可变的类:
class Car
{
string color = "red";
}
当变量直接在类中声明时,它通常被称为字段(或属性)。
它不是必需的,但是在命名类时,最好从大写的第一个字母开始。而且C#文件名和类名匹配是很常见的,因为它使我们的代码有条理。但是它不是必需的(就像在Java中一样)。
创建对象
对象是从类创建的。我们已经创建了名为Car
的类,所以现在可以使用它来创建对象。
要创建Car
的对象,请指定类名,后跟对象名,并使用关键字new
:
- 实例
创建一个名为"myObj
"的对象,并使用它打印color
颜色值:
class Car
{
string color = "red";
static void Main(string[] args)
{
Car myObj = new Car();
Console.WriteLine(myObj.color);
}
}
注意,我们使用点语法(.
)来访问类中的变量/字段(myObj.color
)
多个对象
可以创建一个类的多个对象:
- 实例
创建两个Car
对象:
class Car
{
string color = "red";
static void Main(string[] args)
{
Car myObj1 = new Car();
Car myObj2 = new Car();
Console.WriteLine(myObj1.color);
Console.WriteLine(myObj2.color);
}
}
使用多个类
还可以创建类的对象并在另一个类中访问它。
这通常用于更好的类组织(一个类具有所有字段和方法,而另一个类则持有Main()
方法(要执行的代码))。
- Car.cs
class Car { public string color = "red"; }
- Program.cs
class Program { static void Main(string[] args) { Car myObj = new Car(); Console.WriteLine(myObj.color); } }
类成员
类成员
类中的字段和方法通常称为"类成员":
- 实例
创建一个包含三个类成员的Car
类:两个字段和一个方法。
// 类
class MyClass
{
// 类成员
string color = "red"; // 字段
int maxSpeed = 200; // 字段
public void fullThrottle() // 方法
{
Console.WriteLine("The car is going as fast as it can!");
}
}
字段
在上一章中,了解到类中的变量称为字段,可以通过创建类的对象和使用点语法(.
)来访问它们。
下面的示例将创建一个名为myObj
的Car
类对象。然后打印颜色color
和最大速度maxSpeed
字段的值:
class Car { string color = "red"; int maxSpeed = 200; static void Main(string[] args) { Car myObj = new Car(); Console.WriteLine(myObj.color); Console.WriteLine(myObj.maxSpeed); } }
也可以将字段保留为空,并在创建对象时对其进行修改:
class Car { string color; int maxSpeed; static void Main(string[] args) { Car myObj = new Car(); myObj.color = "red"; myObj.maxSpeed = 200; Console.WriteLine(myObj.color); Console.WriteLine(myObj.maxSpeed); } }
这在创建一个类的多个对象时特别有用:
class Car { string model; string color; int year; static void Main(string[] args) { Car Ford = new Car(); Ford.model = "Mustang"; Ford.color = "red"; Ford.year = 1969; Car Opel = new Car(); Opel.model = "Astra"; Opel.color = "white"; Opel.year = 2005; Console.WriteLine(Ford.model); Console.WriteLine(Opel.model); } }
对象方法
方法通常属于类,它们定义类的对象的行为方式。
与字段一样,可以使用点语法访问方法。但是请注意,该方法必须是公共的public
。请记住,我们使用方法的名称后跟两个paranthese()
和一个分号;
来调用(执行)该方法:
class Car { string color; // 字段 int maxSpeed; // 字段 public void fullThrottle() // 方法 { Console.WriteLine("The car is going as fast as it can!"); } static void Main(string[] args) { Car myObj = new Car(); myObj.fullThrottle(); // 调用方法 } }
为什么我们要声明这个方法是公共的
public
,而不是静态的static
?原因很简单:不创建类的对象就可以访问静态方法
static
,而公共方法public
只能由对象访问。
使用多个类
记住上一章,我们可以使用多个类来更好地组织(一个用于字段和方法,另一个用于执行)。建议:
Car.cs
class Car { public string model; public string color; public int year; public void fullThrottle() { Console.WriteLine("The car is going as fast as it can!"); } }
Program.cs
class Program { static void Main(string[] args) { Car Ford = new Car(); Ford.model = "Mustang"; Ford.color = "red"; Ford.year = 1969; Car Opel = new Car(); Opel.model = "Astra"; Opel.color = "white"; Opel.year = 2005; Console.WriteLine(Ford.model); Console.WriteLine(Opel.model); } }
public
关键字称为访问修饰符,它指定Car
的字段对于其他类(如Program
)也是可访问的。
构造函数
构造函数
构造函数是用于初始化对象的特殊方法。构造函数的优点是在创建类的对象时调用它。它可用于设置字段的初始值:
创建一个构造函数:
// 创建 Car 类 class Car { public string model; // 创建一个字段 // 为 Car 类创建一个类构造函数 public Car() { model = "Mustang"; // 设置 model 的初始值 } static void Main(string[] args) { Car Ford = new Car(); // 创建 Car 类的对象(这将调用构造函数) Console.WriteLine(Ford.model); // 打印 model 的值 } } // 输出 "Mustang"
请注意,构造函数名称必须与类名匹配,并且不能有返回类型(如void或int)。
还要注意,在创建对象时会调用构造函数。
默认情况下,所有类都有构造函数:如果不自己创建类构造函数,C# 将创建一个。但是,不能为字段设置初始值。
节省开发者时间! 看看这一页的最后一个例子来真正理解原因。
构造函数参数
构造函数还可以接受参数,用于初始化字段。
下面的示例向构造函数添加一个字符串参数string modelName
。在构造函数中,我们将model
设置为modelName
(model=modelName
)。当我们调用构造函数时,我们将一个参数传递给构造函数("Mustang"
),它将model
的值设置为"Mustang"
:
class Car { public string model; // 创建一个带参数的类构造函数 public Car(string modelName) { model = modelName; } static void Main(string[] args) { Car Ford = new Car("Mustang"); Console.WriteLine(Ford.model); } } // 输出 "Mustang"
可以有任意多个参数:
class Car { public string model; public string color; public int year; // 创建具有多个参数的类构造函数 public Car(string modelName, string modelColor, int modelYear) { model = modelName; color = modelColor; year = modelYear; } static void Main(string[] args) { Car Ford = new Car("Mustang", "Red", 1969); Console.WriteLine(Ford.color + " " + Ford.year + " " + Ford.model); } } // 输出 Red 1969 Mustang
提示: 与其他方法一样,构造函数可以通过使用不同数量的参数来重载。
访问修饰符
访问修饰符
现在,已经非常熟悉我们的许多示例中出现的public
关键字:
public string color;
public
关键字是一个访问修饰符,用于设置类、字段、方法和属性的访问级别/可见性。
- C# 具有以下访问修饰符:
修饰符 | 描述 |
---|---|
public | 所有类都可以访问该代码 |
private | 代码只能在同一类中访问 |
protected | 代码可以在同一类中访问,也可以在从该类继承的类中访问。将在后面的章节中了解有关继承inheritance 的更多信息 |
internal | 代码只能在它自己的程序集中访问,而不能从另一个程序集中访问。将在后面的一章中了解更多 |
还有两种组合:内部保护protected internal
和私有保护private protected
。
现在,让我们集中讨论公共public
修饰符和私有private
修饰符。
私有修饰符
如果使用私有private
访问修饰符声明字段,则只能在同一类中访问该字段:
class Car
{
private string model;
static void Main(string[] args)
{
Car Ford = new Car("Mustang");
Console.WriteLine(Ford.model);
}
}
输出将是:
Mustang
如果尝试在类外访问它,则会发生错误:
class Car { private string model = "Mustang"; } class Program { static void Main(string[] args) { Car myObj = new Car(); Console.WriteLine(myObj.model); } }
输出将是:
'Car.model' is inaccessible due to its protection level The field 'Car.model' is assigned but its value is never used
公共访问修饰符
如果使用公共public
访问修饰符声明字段,则所有类都可以访问该字段:
class Car
{
public string model = "Mustang";
}
class Program
{
static void Main(string[] args)
{
Car myObj = new Car();
Console.WriteLine(myObj.model);
}
}
输出将是:
Mustang
为什么选择访问修饰符?
控制类成员的可见性(每个单独类和类成员的安全级别)。
实现封装"Encapsulation"——即确保对用户隐藏敏感数据的过程。这是通过将字段声明为
private
来实现的。
注释: 默认情况下,如果不指定访问修饰符,则类的所有成员都是私有的
private
:class Car { string model; // private string year; // private }
C# 属性 (Get 和 Set)
属性和封装
在我们开始解释属性之前,应该对封装"Encapsulation"有一个基本的了解。
封装的意义是确保对用户隐藏敏感数据。为此必须:
- 将字段/变量声明为私有
private
- 通过属性提供公共字段
public
的值,通过get
和set
方法来访问和更新私有字段private
。
属性
private
私有变量只能在同一个类中访问(外部类不能访问它)。但是,有时我们需要访问它们,这可以通过属性来完成。
属性类似于变量和方法的组合,它有两种方法:get
和set
方法:
class Person { private string name; // 字段 public string Name // 属性 { get { return name; } // get 方法 set { name = value; } // set 方法 } }
实例解析
Name
属性与name
字段相关联。好的做法是对属性和私有字段使用相同的名称,但首字母大写。
get
方法返回变量名name
的值。
set
方法为name
变量赋值。value
关键字表示我们分配给属性的值。
现在我们可以使用Name
属性来访问和更新Person
类的private
字段:
class Person { private string name; // 方法 public string Name // 属性 { get { return name; } set { name = value; } } } class Program { static void Main(string[] args) { Person myObj = new Person(); myObj.Name = "Liam"; Console.WriteLine(myObj.Name); } }
输出为:
Liam
自动属性(简写)
C#还提供了一种使用简写/自动属性的方法,不必为属性定义字段,只需在属性内部编写 get;
和set;
。
下面的示例将产生与上面示例相同的结果。唯一的区别是代码更少:
class Person
{
public string Name // property
{ get; set; }
}
class Program
{
static void Main(string[] args)
{
Person myObj = new Person();
myObj.Name = "Liam";
Console.WriteLine(myObj.Name);
}
}
输出将是:
Liam
为什么要用封装?
- 更好地控制类成员(减少自己(或他人)弄乱代码的可能性)
- 字段可以设置为只读(如果只使用
get
方法),也可以设置为只读(如果只使用set
方法) - 灵活:程序员可以更改代码的一部分而不影响其他部分
- 提高了数据的安全性
继承
继承(派生类和基类)
在 C# 中,可以将字段和方法从一个类继承到另一个类。我们将继承概念分为两类:
- 派生类(子类)-从另一个类继承的类
- 基类(父级)-从中继承的类
要从类继承,请使用:
符号。
在下面的示例中Car
类(child)从Vehicle
类(parent)继承字段和方法:
class Vehicle // 基类(父类) { public string brand = "Ford"; // Vehicle 字段 public void honk() // Vehicle 方法 { Console.WriteLine("Tuut, tuut!"); } } class Car : Vehicle // 派生类(子) { public string modelName = "Mustang"; // Car 字段 } class Program { static void Main(string[] args) { // 创建一个 myCar 对象 Car myCar = new Car(); // 在 myCar 对象上调用 honk() 方法(来自 Vehicle 类) myCar.honk(); // 显示品牌字段的值(来自 Vehicle 类)和来自 Car 类的 modelName 的值 Console.WriteLine(myCar.brand + " " + myCar.modelName); } }
- 何时使用继承?
它对代码的可重用性很有用:在创建新类时重用现有类的字段和方法。
多态
多态性和重写方法
多态性意味着"许多形式",当我们有许多类通过继承相互关联时,多态性就会出现。
继承允许我们从另一个类继承字段和方法。多态性使用这些方法来执行不同的任务。这允许我们以不同的方式执行单个操作。
例如一个名为Animal
的基类,它有一个名为animalSound()
的方法。衍生的动物可以是猪、猫、狗、鸟,它们也有自己的动物声音(猪叫声,猫叫等)的实现:
class Animal // 基类(父类) { public void animalSound() { Console.WriteLine("The animal makes a sound"); } } class Pig : Animal // 派生类(子) { public void animalSound() { Console.WriteLine("The pig says: wee wee"); } } class Dog : Animal // 派生类(子) { public void animalSound() { Console.WriteLine("The dog says: bow wow"); } }
现在我们可以创建Pig
和Dog
对象,并对它们调用animalSound()
方法:
class Animal // 基类(父类) { public void animalSound() { Console.WriteLine("The animal makes a sound"); } } class Pig : Animal // 派生类(子) { public void animalSound() { Console.WriteLine("The pig says: wee wee"); } } class Dog : Animal // 派生类(子) { public void animalSound() { Console.WriteLine("The dog says: bow wow"); } } class Program { static void Main(string[] args) { Animal myAnimal = new Animal(); // 创建一个 Animal 对象 Animal myPig = new Pig(); // 创建 Pig 对象 Animal myDog = new Dog(); // 创建 Dog 对象 myAnimal.animalSound(); myPig.animalSound(); myDog.animalSound(); } }
输出将是:
The animal makes a sound
The animal makes a sound
The animal makes a sound
- 为什么不是我要的结果?
上面示例的输出可能不是所期望的。这是因为基类方法在派生类方法共享相同名称时覆盖了它们。
但是,C#提供了一个重写基类方法的选项,方法是将
virtual
关键字添加到基类内的方法,并对每个派生类方法使用override
关键字:class Animal // 基类(父类) { public virtual void animalSound() { Console.WriteLine("The animal makes a sound"); } } class Pig : Animal // 派生类(子) { public override void animalSound() { Console.WriteLine("The pig says: wee wee"); } } class Dog : Animal // 派生类(子) { public override void animalSound() { Console.WriteLine("The dog says: bow wow"); } } class Program { static void Main(string[] args) { Animal myAnimal = new Animal(); // 创建一个 Animal 对象 Animal myPig = new Pig(); // 创建 Pig 对象 Animal myDog = new Dog(); // 创建 Dog 对象 myAnimal.animalSound(); myPig.animalSound(); myDog.animalSound(); } }
输出将是:
The animal makes a sound
The pig says: wee wee
The dog says: bow wow
为什么使用"继承"和"多态"?
它对代码的可重用性很有用:在创建新类时重用现有类的字段和方法。
抽象类
抽象类和方法
数据抽象是隐藏某些细节并只向用户显示基本信息的过程。
抽象可以通过抽象类或接口来实现(将在下一章了解更多)。
abstract
关键字用于类和方法:
- 抽象类:是一个受限类,不能用于创建对象(要访问它,必须从另一个类继承)。
- 抽象方法:只能在抽象类中使用,并且它没有主体。主体由派生类(继承自)提供。
抽象类可以有抽象方法和正则方法:
abstract class Animal { public abstract void animalSound(); public void sleep() { Console.WriteLine("Zzz"); } }
从上面的例子中,无法创建 Animal 类的对象:
Animal myObj = new Animal(); // 会产生错误(无法创建抽象类或接口'Animal'的实例)
要访问抽象类,它必须从另一个类继承。 让我们将我们在多态性一章中使用的Animal类转换为一个抽象类。
记得在继承章节中我们使用
:
符号来继承一个类, 并且我们使用override
关键字来覆盖基类方法。
// 抽象类 abstract class Animal { // 抽象方法(没有主体) public abstract void animalSound(); // 常规方法 public void sleep() { Console.WriteLine("Zzz"); } } // 派生类(继承自 Animal) class Pig : Animal { public override void animalSound() { // 这里提供了 animalSound() 的主体 Console.WriteLine("The pig says: wee wee"); } } class Program { static void Main(string[] args) { Pig myPig = new Pig(); // 创建 Pig 对象 myPig.animalSound(); // 调用抽象方法 myPig.sleep(); // 调用常规方法 } }
- 为什么使用抽象类和方法?
为了实现安全性-隐藏某些细节,只显示对象的重要细节。
注释: 抽象也可以通过接口来实现,
接口
接口
在 C# 中实现抽象的另一种方法是使用接口。
接口是一个完全抽象类,它只能包含抽象方法和属性(具有空体):
// 接口 interface Animal { void animalSound(); // 接口方法(没有主体) void run(); // 接口方法(没有主体) }
在接口的开头用字母"I"开头被认为是一种好的做法,因为这样可以更容易记住它是一个接口而不是一个类。
默认情况下,接口的成员是抽象的
abstract
和公共的public
。注释: 接口可以包含属性和方法,但不能包含字段。
要访问接口方法,接口必须由另一个类实现(类似于继承)。要实现接口,请使用
:
符号(与继承一样)。接口方法的主体由"implement"类提供。 注意,在实现接口时,不必使用override
关键字:
// 接口 interface IAnimal { void animalSound(); // 接口方法(没有主体) } // Pig "实现"了 IAnimal 接口 class Pig : IAnimal { public void animalSound() { // 这里提供了 animalSound() 的主体 Console.WriteLine("The pig says: wee wee"); } } class Program { static void Main(string[] args) { Pig myPig = new Pig(); // 创建 Pig 对象 myPig.animalSound(); } }
接口说明:
- 与抽象类一样,接口不能用于创建对象(在上面的示例中,不可能在Program类中创建"IAnimal"对象)
- 接口方法没有主体-主体由"implement"类提供
- 在实现接口时,必须重写其所有方法
- 接口可以包含属性和方法,但不能包含字段/变量
- 默认情况下,接口成员是抽象的
abstract
和公共的public
- 接口不能包含构造函数(因为它不能用于创建对象)
为什么以及何时使用接口?
\1) 为了实现安全性-隐藏某些细节,只显示对象(接口)的重要细节。
\2) C#不支持多重继承(一个类只能从一个基类继承)。但是,它可以通过接口实现,因为类可以实现多个接口。注释:要实现多个接口,请用逗号分隔它们(参见下面的示例)。
多个接口
要实现多个接口,请用逗号分隔它们:
interface IFirstInterface
{
void myMethod(); // 接口方法
}
interface ISecondInterface
{
void myOtherMethod(); // 接口方法
}
// 实现多个接口
class DemoClass : IFirstInterface, ISecondInterface
{
public void myMethod()
{
Console.WriteLine("Some text..");
}
public void myOtherMethod()
{
Console.WriteLine("Some other text...");
}
}
class Program
{
static void Main(string[] args)
{
DemoClass myObj = new DemoClass();
myObj.myMethod();
myObj.myOtherMethod();
}
}
C# 枚举
枚举
枚举enum
是一个特殊的类"class",它表示一组常量(不可更改/只读变量)。
要创建枚举enum
,请使用enum
关键字(而不是class或interface),并用逗号分隔枚举项:
enum Level
{
Low,
Medium,
High
}
你可以使用**.**点语法访问枚举:
Level myVar = Level.Medium;
Console.WriteLine(myVar);
Enum 是 “enumerations” 的缩写,意思是特别列出的。
类内枚举
类中还可以有枚举enum
:
class Program
{
enum Level
{
Low,
Medium,
High
}
static void Main(string[] args)
{
Level myVar = Level.Medium;
Console.WriteLine(myVar);
}
}
输出为:
Medium
枚举值
默认情况下,枚举的第一项的值为0,第二个值为1,依此类推。
要从项中获取整数值,必须将该项显式转换为int
:
enum Months { January, // 0 February, // 1 March, // 2 April, // 3 May, // 4 June, // 5 July // 6 } static void Main(string[] args) { int myNum = (int) Months.April; Console.WriteLine(myNum); }
输出将是:
3
还可以指定自己的枚举值,接下来的项目将相应地更新该数字:
enum Months { January, // 0 February, // 1 March=6, // 6 April, // 7 May, // 8 June, // 9 July // 10 } static void Main(string[] args) { int myNum = (int) Months.April; Console.WriteLine(myNum); }
输出将是:
7
Switch 语句中的枚举
枚举通常在switch
语句中用于检查相应的值:
enum Level
{
Low,
Medium,
High
}
static void Main(string[] args)
{
Level myVar = Level.Medium;
switch(myVar)
{
case Level.Low:
Console.WriteLine("Low level");
break;
case Level.Medium:
Console.WriteLine("Medium level");
break;
case Level.High:
Console.WriteLine("High level");
break;
}
}
输出将是:
Medium level
为什么以及何时使用枚举?
当知道值不会更改时,可以使用枚举,比如月份、星期、颜色、卡片组等。