【从零开始入门unity游戏开发之——C#篇23】C#面向对象继承——`as`类型转化和`is`类型检查、向上转型和向下转型、里氏替换原则(LSP)
文章目录
- 一、`as`类型转化和`is`类型检查
- 1、`as` 关键字
- 使用场景:
- 语法:
- 示例:
- 特点:
- 2、`is` 关键字
- 使用场景:
- 语法:
- 示例:
- 特点:
- 3、总结
- 二、向上转型和向下转型
- 1、向上转型
- 示例:
- 2、向下转型
- 示例:
- 3、向下转型的安全性
- 使用 `is` 操作符:
- 使用 `as` 操作符:
- 4、总结
- 三、里氏替换原则
- 1、什么是里氏替换原则?
- 2、**为什么要遵循里氏替换原则?**
- 2.1 **程序更可靠**
- 2.2 **更容易维护**
- 2.3 **支持扩展**
- 3、**举个例子**
- 4、**总结**
- 专栏推荐
- 完结
一、as
类型转化和is
类型检查
在C#中,as
和is
是两种用于类型转换和类型检查的关键字,它们的作用和用法有所不同。下面是这两个关键字的详细介绍:
1、as
关键字
as
关键字用于进行类型转换,通常用于将一个对象转换为指定的类型。如果转换成功,返回转换后的对象;如果转换失败,返回 null
。
使用场景:
as
用于安全的类型转换,可以避免抛出异常。- 它通常用于
引用类型的转换
(包括接口转换),而不适用于值类型(如int
、double
等)。
语法:
object obj = someObject;
SomeClass objAsClass = obj as SomeClass;
如果 obj
是 SomeClass
类型的实例,objAsClass
将引用该实例;否则,它将是 null
。
示例:
class Animal { }
class Dog : Animal { }
Animal animal = new Dog();
Dog dog = animal as Dog; // 成功转换,dog 不为 null
object obj = "Hello";
string str = obj as string; // 成功转换,str 为 "Hello"
int? num = obj as int?; // 转换失败,num 为 null
特点:
- 如果转换失败,
as
不会抛出异常,而是返回null
。 - 适用于引用类型和可空值类型(
Nullable<T>
)。
2、is
关键字
is
关键字用于检查对象是否可以转换为指定的类型,返回一个布尔值(true
或 false
)。如果对象是指定类型的实例,返回 true
,否则返回 false
。
使用场景:
is
常用于类型检查,在执行类型转换之前验证对象是否符合预期类型。- 适用于所有类型(
值类型
和引用类型
)。
语法:
if (obj is SomeClass) {
// obj 是 SomeClass 类型
}
示例:
object obj = "Hello";
if (obj is string) {
Console.WriteLine("obj 是 string 类型" );
} else {
Console.WriteLine("obj 不是 string 类型");
}
if (obj is int) {
Console.WriteLine("obj 是 int 类型");
} else {
Console.WriteLine("obj 不是 int 类型");
}
在C# 7.0及更高版本中,is
关键字还可以在检查类型的同时进行类型转换。例如:
object obj = "Hello";
if (obj is string str) {
Console.WriteLine($"字符串值: {str}");
}
特点:
is
在检查类型时,不会抛出异常。- 如果使用
is
进行类型检查后,成功匹配的类型可以直接赋值给变量(如上面的str
),避免了显式的转换。
3、总结
- 使用
as
来进行类型转换时,它不会抛出异常。如果转换成功,它会返回转换后的对象;如果转换失败,它会返回null
。 - 使用
is
来进行类型检查时,它会返回一个布尔值,可以在检查通过时直接进行类型转换,避免了额外的类型转换步骤。
这两者通常配合使用,以确保类型转换的安全性和准确性。
二、向上转型和向下转型
在 C# 中,向上转型和向下转型是涉及类型转换的两个概念,通常与类的继承和多态性相关。
1、向上转型
向上转型是指将一个派生类对象转换为其基类类型。由于派生类继承了基类的所有成员(字段、属性、方法等),因此,派生类对象可以被当作基类对象来使用。
向上转型是安全的,通常是不需要显式的类型转换,因为编译器会自动进行转换。
示例:
class Animal
{
public void Speak()
{
Console.WriteLine("Animal speaks");
}
}
class Dog : Animal
{
public void Bark()
{
Console.WriteLine("Dog barks");
}
}
class Program
{
static void Main()
{
Dog dog = new Dog();
// 向上转型
Animal animal = dog; // Dog 转换为 Animal
animal.Speak(); // Animal 的方法
// animal.Bark(); // 编译错误,因为 animal 是 Animal 类型,不能调用 Dog 的方法
}
}
在这个例子中,dog
对象被向上转型为 Animal
类型。通过 animal
变量,我们只能访问 Animal
类中的成员,而无法访问 Dog
类特有的方法(如 Bark()
)。
2、向下转型
向下转型是指将一个基类类型的对象转换为派生类类型。向下转型需要显式进行,因为基类类型的变量可能包含多个不同的派生类实例,而编译器无法确定转换的安全性。
向下转型通常会涉及到检查运行时类型,因为如果对象的实际类型不是目标类型的实例,强制转换会引发运行时异常。
示例:
class Animal
{
public void Speak()
{
Console.WriteLine("Animal speaks");
}
}
class Dog : Animal
{
public void Bark()
{
Console.WriteLine("Dog barks");
}
}
class Program
{
static void Main()
{
Animal animal = new Dog(); // 向上转型
Dog dog = (Dog)animal; // 向下转型,显式转换
dog.Bark(); // 可以调用 Dog 的方法
// 以下代码会抛出 InvalidCastException 异常
// Animal animal2 = new Animal();
// Dog dog2 = (Dog)animal2; // 异常,因为 animal2 实际上不是 Dog 类型
}
}
3、向下转型的安全性
为了避免 InvalidCastException
异常,可以使用 is
或 as
操作符进行安全的类型转换。
使用 is
操作符:
is
用于检查对象是否可以转换为指定的类型。如果可以,则返回 true
,否则返回 false
。
Animal animal = new Dog();
if (animal is Dog dog)
{
dog.Bark(); // 安全地调用 Bark()
}
else
{
Console.WriteLine("animal 不是 Dog 类型");
}
使用 as
操作符:
as
操作符会尝试将对象转换为指定的类型。如果不能转换,则返回 null
,而不会抛出异常。
先判断,在转换。(它比传统的强制转换相对来说要安全一点,因为传统的强制转换, 一旦转换失败的话,程序就会崩溃,那么使用as关键字,如果转换不成功,就转换成空类型)
Animal animal = new Dog();
Dog dog = animal as Dog; // 如果 animal 不是 Dog 类型,则 dog 为 null
if (dog != null)
{
dog.Bark(); // 安全地调用 Bark()
}
else
{
Console.WriteLine("animal 不是 Dog 类型");
}
4、总结
- 向上转型:将派生类对象转换为基类类型,编译器自动进行转换,通常是安全的。
- 向下转型:将基类对象转换为派生类类型,需要显式转换,可能会抛出
InvalidCastException
,因此需要使用is
或as
进行安全检查。
三、里氏替换原则
里氏替换原则(Liskov Substitution Principle,LSP)是面向对象设计中的五大原则之一,它在设计和实现继承体系时起着至关重要的作用。遵循里氏替换原则能确保系统的稳定性、扩展性和可维护性。
1、什么是里氏替换原则?
里氏替换原则是面向对象编程中一个非常重要的设计原则,它的意思是:
如果一个类是另一个类的子类,那么在程序中可以用子类的实例来替换父类的实例,而且程序的行为不会改变。
简单来说,里氏替换原则要求:你可以用子类(从父类继承的类)替换掉父类,而不会让程序出错
。
举个简单的例子:
假设你有一个父类 动物
,有一个方法 叫()
。然后你有一个子类 狗
,它也有 叫()
方法。当你把一个 狗
对象传给需要 动物
对象的函数时,这个函数依然能正常工作,不会因为传入了 狗
而出错。
2、为什么要遵循里氏替换原则?
2.1 程序更可靠
如果子类没有遵循里氏替换原则,程序可能会因为传入子类对象而出错。遵循这个原则,可以确保子类对象可以正常替代父类对象,程序不会崩溃或出现奇怪的行为。
2.2 更容易维护
如果每个子类都能正确替换父类,程序的结构更简单,修改一个部分时不会影响到其他地方,代码更加稳定。例如,你可以在不改变其他代码的情况下,换成不同的子类,程序依然能正常工作。
2.3 支持扩展
当你遵循里氏替换原则时,未来增加新的子类(比如 猫
继承 动物
)也会很容易,新的子类会和旧的代码兼容,不会引起问题。
3、举个例子
假设有一个程序需要打印出所有动物叫的声音:
class Animal {
public virtual void MakeSound() {
Console.WriteLine("Some sound");
}
}
class Dog : Animal {
public override void MakeSound() {
Console.WriteLine("Bark");
}
}
class Cat : Animal {
public override void MakeSound() {
Console.WriteLine("Meow");
}
}
void PrintSound(Animal animal) {
animal.MakeSound();
}
在这个例子里,Dog
和 Cat
都继承自 Animal
,并且实现了 MakeSound()
方法。你可以把 Dog
或 Cat
传给 PrintSound()
,程序依然会正常工作。
如果里氏替换原则被破坏,比如 Dog
类修改了父类的行为,导致无法在 PrintSound()
中正常工作,那就说明没有遵循这个原则,程序可能会出错或表现得不对。
4、总结
- 里氏替换原则确保子类可以无缝替换父类而不破坏程序行为。
- 它让你的代码更稳定、容易维护和扩展。
- 你可以放心地使用不同的子类,而不必担心它们会影响到程序的其他部分。
专栏推荐
地址 |
---|
【从零开始入门unity游戏开发之——C#篇】 |
【从零开始入门unity游戏开发之——unity篇】 |
【制作100个Unity游戏】 |
【推荐100个unity插件】 |
【实现100个unity特效】 |
【unity框架开发】 |
完结
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注
,你的每一次支持
都是我不断创作的最大动力。当然如果你发现了文章中存在错误
或者有更好的解决方法
,也欢迎评论私信告诉我哦!
好了,我是向宇
,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!如果你遇到任何问题,也欢迎你评论私信或者加群找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~