第壹章第12节 C#和TS语言对比-多态

多态概念比较抽象,我的经验是,不要去试图理解文字概念,我也没见过能用文字说清楚的,直接用代码干就是,熟悉几个套路用法,自然而然就明白了。

一、C#中的多态

1.1 多态基本使用

C#中,和多态相关的特性很多,比如之前讲到的方法重载、虚方法重写、隐藏方法、抽象方法实现、接口实现,都可以归到多态范畴。但实操来说,我觉得应该把范围缩窄,多态就是下面这种情况:基类变量可持有派生类的实例对象并调用其方法;接口变量也可以持有实现类的实例对象并调用其方法。

//基类Animal-----------------------------------------
public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("Animal makes a sound.");
    }
}
//派生类Cat------------------------------------------
public class Cat : Animal
{
    public override void MakeSound() //重写基类方法
    {
        Console.WriteLine("Meow!");
    }
}
//派生类Dog------------------------------------------
public class Dog : Animal
{
    public override void MakeSound() //重写基类方法
    {
        Console.WriteLine("Woof! Woof!");
    }
}
//省略入口方法----------------------------------------
//一般使用
Animal animal = new Animal();
animal.MakeSound(); // 输出: Animal makes a sound.
cat cat = new Cat();
cat.MakeSound();    // 输出: Meow!
Dog dog = new Dog();
dog.MakeSound();    // 输出: Woof! Woof!
//***多态使用,将子类对象赋给父类变量,并调用方法
Animal a1 = new Cat();
a1.MakeSound(); // 输出: Meow!
Animal a2 = new Dog();
a2.MakeSound(); // 输出: Woof! Woof!

1.2 多态究竟有什么用

前面的案例,不能说明多态究竟有啥用!前面有说,接口和实现类之间,也有多态特性,“接口变量也可以持有实现类的实例对象并调用其方法”。所以,下面以接口为例,直接上.NET中的依赖注入IOC。

//定义依赖注入的服务类和实现类,这里服务类是接口=======================================
//定义一个接口ITestService.cs
interface ITestService
{
    void SayHi();
}
//定义第一个实现接口的类TestService1.cs,待会作为依赖注入的实现类,
public class TestService1 : ITestService
{
    public void SayHi()
    {
        Console.WriteLine("Hi,我是Service-1");
    }
}
//定义第二个实现接口的类TestService2.cs,待会作为依赖注入的实现类
public class TestService2 : ITestService
{
    public void SayHi()
    {
        Console.WriteLine("Hi,我是Service-2");
    }
}


//AspNetCoe中注册服务和调用服务======================================================
var builder = WebApplication.CreateBuilder(args); 
builder.Services.AddScoped<ITestService,TestService>();
var app = builder.Build();
app.MapGet("/", (ITestService testService) =>{var names = testService.SayHi;});
app.Run();


//builder.Services是由框架为我们提供的IOC容器
//调用AddScoped方法,注册了一个服务,服务类是ITestService,实现类是TestService1
//代码1:
builder.Services.AddScoped<ITestService,TestService1>();


//"(ITestService testService)",这段代码向IOC容器要ITestService
//IOC容器会自动帮我们做下面的事情:ITestService testService = new TestService1()
//代码2:
app.MapGet("/", (ITestService testService) =>{var names = testService.SayHi;});


//再解释一下它的意义:
/*
 代码2,是实际的业务代码。我们会在项目的很多地方用到TestService1或者TestService2
 如果不用依赖注入:
 -假设我们在项目的100个地方用到,就需要new TestService1(),100次
 -突然项目变更,现在要说“Hi,我是Service-2”,你需要在100个地方,改为new TestService2()
 如果使用依赖注入:
 -我们只需要修改一行代码,builder.Services.AddScoped<ITestService,TestService2>();
 -把实现类由TestService1修改为TestService2即可


//不多说了,再说就是废话了!!!

二、TS中的多态

1.1 TS的类型兼容概念

JS是动态类型,没有多态概念,因为它“全多态”。TS为JS带来了强类型,所以也需要多态来增加灵活性,在TS中,称之为类型兼容。由于TS只是为JS添加的类型,本质还是一样,所以它的多态和C#很不一样。TS用的是结构化类型系统,也叫鸭子类型,类型检查的是对象的形状是否相同或相似,不要求类型之间是否有继承或者实现关系。结构化类型是标明类型的超集,上面讲的C#的多态,在TS中一样适用。至于TS的依赖注入,前端几乎没用过,但nest.js使用了依赖注入,我只了解了一下,实际没用过nest.js做后端,就不展开了。由于TS的类型兼容太过灵活了,实际开发中,如果大量用到类型兼容,很容易被搞疯。如果要用,我的建议是把范围缩小到和C#一样,仅用在有继承和实现关系的类型上。

1.2 TS特有的类型兼容

下例中,只讲C#中没有的类型兼容,了解一下即可,反正我的项目是严格要求不要这么用。

//1、class的类型兼容==================================================================
//兼容和多态是一个概念,在TS中,多使用兼容

//如果对象具有相同的形状,则属于同一类型---------------
class Point {x:nmuber; y:number}
class Point2D {x:nmuber; y:number}
const p:Point = new Point2D() //C#中是不可能的

//对象具有的形状,有多有少----------------------------
class Point {x:nmuber; y:number}
class Point3D {x:nmuber; y:number; z:number}
const p:Point = new Point3D() //对象多的,可以赋值给对象少的,可以将Point3D视为子类
//const p:Point3D = new Point() //报错,反过来,对象少的不能赋值给对象多的


//2、接口的类型兼容===================================================================
//和类的兼容性类似
interface Point {x:nmuber; y:number}
interface Point2D {x:nmuber; y:number}
let p1:Point
let p2:Point2D = p1

interface Point2D {x:nmuber; y:number; z:nmuber}
let p3:Point3D
p2 = p3

class Point3D {x:nmuber; y:number; z:nmuber}
let p3:Point2D = new Point3D() //class和interface之间也可以兼容


//3、函数的类型兼容===================================================================
//函数类型兼容要同时满足参数个数、类型和返回值的兼容
//最让人发疯的就是这个了

//①参数个数兼容,参数少的可以赋值给参数多的-------------------
type F1 = (a:number)=>void
type F2 = (a:number, b:number)=>void
let f1:F1
let f2:F2 =f1
//记住forEach()方法即可
const arr = [1,2,3,4]
arr.forEach(()=>{})
arr.forEach((item)=>{})

//②参数类型兼容----------------------------------------------
//如果参数是值类型,则要求类型要相同
//如果参数是对象类型,则将对象的属性拆开,然后按值类型和参数个数来检测
//如果是有嵌套的复杂类型呢,按以上规则来拆,已疯!!!
//不举例了

//③参数返回值兼容-------------------------------------------
//如果返回值是值类型,则要求类型要相同
//如果返回值是对象类型,则兼容性同class/interface的兼容
//不举例了

//关于函数兼容总结:知道forEach方法是如何定义及使用的就可以了

  • 19
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值