【从零开始入门unity游戏开发之——C#篇23】C#面向对象继承——`as`类型转化和`is`类型检查、向上转型和向下转型、里氏替换原则(LSP)

文章目录

一、as类型转化和is类型检查

在C#中,asis是两种用于类型转换和类型检查的关键字,它们的作用和用法有所不同。下面是这两个关键字的详细介绍:

1、as 关键字

as 关键字用于进行类型转换,通常用于将一个对象转换为指定的类型。如果转换成功,返回转换后的对象;如果转换失败,返回 null

使用场景:

  • as 用于安全的类型转换,可以避免抛出异常。
  • 它通常用于引用类型的转换(包括接口转换),而不适用于值类型(如 intdouble 等)。

语法:

csharp 复制代码
object obj = someObject;
SomeClass objAsClass = obj as SomeClass;

如果 objSomeClass 类型的实例,objAsClass 将引用该实例;否则,它将是 null

示例:

csharp 复制代码
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 关键字用于检查对象是否可以转换为指定的类型,返回一个布尔值(truefalse)。如果对象是指定类型的实例,返回 true,否则返回 false

使用场景:

  • is 常用于类型检查,在执行类型转换之前验证对象是否符合预期类型。
  • 适用于所有类型(值类型引用类型)。

语法:

csharp 复制代码
if (obj is SomeClass) {
    // obj 是 SomeClass 类型
}

示例:

csharp 复制代码
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 关键字还可以在检查类型的同时进行类型转换。例如:

csharp 复制代码
object obj = "Hello";
if (obj is string str) {
    Console.WriteLine($"字符串值: {str}");
}

特点:

  • is 在检查类型时,不会抛出异常。
  • 如果使用 is 进行类型检查后,成功匹配的类型可以直接赋值给变量(如上面的 str),避免了显式的转换。

3、总结

  • 使用 as 来进行类型转换时,它不会抛出异常。如果转换成功,它会返回转换后的对象;如果转换失败,它会返回 null
  • 使用 is 来进行类型检查时,它会返回一个布尔值,可以在检查通过时直接进行类型转换,避免了额外的类型转换步骤。

这两者通常配合使用,以确保类型转换的安全性和准确性。


二、向上转型和向下转型

在 C# 中,向上转型向下转型是涉及类型转换的两个概念,通常与类的继承和多态性相关。

1、向上转型

向上转型是指将一个派生类对象转换为其基类类型。由于派生类继承了基类的所有成员(字段、属性、方法等),因此,派生类对象可以被当作基类对象来使用。

向上转型是安全的,通常是不需要显式的类型转换,因为编译器会自动进行转换。

示例:

csharp 复制代码
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、向下转型

向下转型是指将一个基类类型的对象转换为派生类类型。向下转型需要显式进行,因为基类类型的变量可能包含多个不同的派生类实例,而编译器无法确定转换的安全性。

向下转型通常会涉及到检查运行时类型,因为如果对象的实际类型不是目标类型的实例,强制转换会引发运行时异常。

示例:

csharp 复制代码
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 异常,可以使用 isas 操作符进行安全的类型转换。

使用 is 操作符:

is 用于检查对象是否可以转换为指定的类型。如果可以,则返回 true,否则返回 false

csharp 复制代码
Animal animal = new Dog();
if (animal is Dog dog)
{
    dog.Bark(); // 安全地调用 Bark()
}
else
{
    Console.WriteLine("animal 不是 Dog 类型");
}

使用 as 操作符:

as 操作符会尝试将对象转换为指定的类型。如果不能转换,则返回 null,而不会抛出异常。

先判断,在转换。(它比传统的强制转换相对来说要安全一点,因为传统的强制转换, 一旦转换失败的话,程序就会崩溃,那么使用as关键字,如果转换不成功,就转换成空类型)

csharp 复制代码
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,因此需要使用 isas 进行安全检查。

三、里氏替换原则

里氏替换原则(Liskov Substitution Principle,LSP)是面向对象设计中的五大原则之一,它在设计和实现继承体系时起着至关重要的作用。遵循里氏替换原则能确保系统的稳定性、扩展性和可维护性。

1、什么是里氏替换原则?

里氏替换原则是面向对象编程中一个非常重要的设计原则,它的意思是:

如果一个类是另一个类的子类,那么在程序中可以用子类的实例来替换父类的实例,而且程序的行为不会改变。

简单来说,里氏替换原则 要求:你可以用子类(从父类继承的类)替换掉父类,而不会让程序出错

举个简单的例子:

假设你有一个父类 动物,有一个方法 叫()。然后你有一个子类 ,它也有 叫() 方法。当你把一个 对象传给需要 动物 对象的函数时,这个函数依然能正常工作,不会因为传入了 而出错。

2、为什么要遵循里氏替换原则?

2.1 程序更可靠

如果子类没有遵循里氏替换原则,程序可能会因为传入子类对象而出错。遵循这个原则,可以确保子类对象可以正常替代父类对象,程序不会崩溃或出现奇怪的行为。

2.2 更容易维护

如果每个子类都能正确替换父类,程序的结构更简单,修改一个部分时不会影响到其他地方,代码更加稳定。例如,你可以在不改变其他代码的情况下,换成不同的子类,程序依然能正常工作。

2.3 支持扩展

当你遵循里氏替换原则时,未来增加新的子类(比如 继承 动物)也会很容易,新的子类会和旧的代码兼容,不会引起问题。

3、举个例子

假设有一个程序需要打印出所有动物叫的声音:

csharp 复制代码
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();
}

在这个例子里,DogCat 都继承自 Animal,并且实现了 MakeSound() 方法。你可以把 DogCat 传给 PrintSound(),程序依然会正常工作。

如果里氏替换原则被破坏,比如 Dog 类修改了父类的行为,导致无法在 PrintSound() 中正常工作,那就说明没有遵循这个原则,程序可能会出错或表现得不对。

4、总结

  • 里氏替换原则确保子类可以无缝替换父类而不破坏程序行为。
  • 它让你的代码更稳定、容易维护和扩展。
  • 你可以放心地使用不同的子类,而不必担心它们会影响到程序的其他部分。

专栏推荐

地址
【从零开始入门unity游戏开发之------C#篇】
【从零开始入门unity游戏开发之------unity篇】
【制作100个Unity游戏】
【推荐100个unity插件】
【实现100个unity特效】
【unity框架开发】

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!如果你遇到任何问题,也欢迎你评论私信或者加群找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~

相关推荐
筏镜7 分钟前
调整docker bridge地址冲突,通过bip调整 bridge地址
java·docker·eureka
水木流年追梦12 分钟前
【python因果库实战10】为何需要因果分析
开发语言·python
winner888116 分钟前
git merge 冲突 解决 show case
java·git·git merge·git冲突
w(゚Д゚)w吓洗宝宝了1 小时前
C vs C++: 一场编程语言的演变与对比
c语言·开发语言·c++
AI人H哥会Java2 小时前
【Spring】Spring的模块架构与生态圈—Spring MVC与Spring WebFlux
java·开发语言·后端·spring·架构
开心工作室_kaic2 小时前
springboot461学生成绩分析和弱项辅助系统设计(论文+源码)_kaic
开发语言·数据库·vue.js·php·apache
毕设资源大全2 小时前
基于SpringBoot+html+vue实现的林业产品推荐系统【源码+文档+数据库文件+包部署成功+答疑解惑问到会为止】
java·数据库·vue.js·spring boot·后端·mysql·html
Watermelon_Mr2 小时前
Spring(三)-SpringWeb-概述、特点、搭建、运行流程、组件、接受请求、获取请求数据、特殊处理、拦截器
java·后端·spring
觉醒的程序猿2 小时前
vue2设置拖拽选中时间区域
开发语言·前端·javascript
明月看潮生3 小时前
青少年编程与数学 02-004 Go语言Web编程 12课题、本地数据存储
开发语言·青少年编程·本地存储·编程与数学·goweb