深入了解类

静态方法static

静态方法是全局的,调用时无需声明对象,直接通过 类名 . 静态方法名,即可调用

 

虚函数virtual

一般虚函数加virtual关键字,写在父类中,然后之内可以通过关键字override对它进行重写,外部调用时从而可以从改变它功能。假如子类没有对它进行重写,则之类对象调用的就是父类中的功能。下图是子类中重写了父类的Calculate函数,但是仍然继续调用父类的Calculate

class Vehcle//交通工具

{

public virtual void Run()

{

Console.WriteLine("Vehcle Runing!!!!");

}

}

class Car : Vehcle//机动车

{

public override void Run()

{

Console.WriteLine("car Runing!!!!");

}

}

class RaseCar : Car//跑车

{

public override void Run()

{

Console.WriteLine("RaseCar Runing!!!!");

}

}

调用:

Vehcle V = new Vehcle();

V.Run();//输出Vehcle Runing!!! 虚函数实体可调用

 

Vehcle V1 = new Vehcle();

V1.Run();//输出car Runing!!!! 继承并重写虚函数后,可以用父类的类型接收子类对象,调用时由最后重写的子类决定

 

 

Vehcle V2 = new RaseCar();

V2.Run();//输出RaseCar Runing!!!! 继承并重写虚函数后,可以用父类的类型接收子类对象,调用时由最后重写的子类决定

 

override也叫做多态,override必须满足三个条件:

  1. 函数或者属性成员
  2. 可见,父类中必须为public不能时private
  3. 重写函数与父类的函数签名一致(函数参数个数以及类型必须一样)

 

用父类的类型接收子类对象的好处

假如现在有两个数组:

int[] num1 = new int[] { 1, 2, 3, 4, 5 };

ArrayList num2 = new ArrayList { 6, 7, 8, 9, 10 };

现在需要写一个函数对数组求和,由于择两个数组时不同的类型,所以需要写两个函数,不同类型用用不同函数,如下。

static int Sum1(int[] nums)

{

int sum = 0;

foreach (var n in nums)

{

sum += n;

}

return sum;

}

static int Sum2(ArrayList nums)

{

int sum = 0;

foreach (var n in nums)

{

sum +=(int) n;

}

return sum;

}

解决办法:

int[]转到定义:

public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<Int32>, IEquatable<Int32>

ArrayList转到定义:

public class ArrayList : IList, ICollection, IEnumerable, Icloneable

发现他们多是继承了Ienumerable,所以Ienumerable类型就都可以接收以上两种类型的对象

改进方法:

static int Sum(IEnumerable nums)

{

int sum = 0;

foreach (var n in nums)

{

sum += (int)n;

}

return sum;

}

 

 

 

抽象类/方法abstract

抽象方法一定要写在抽象类中,且抽象方法不能有方法体。所在抽象类也不能new对象

    调用时:

 

接口interface

定义一个接口,在接口中写函数。函数不能有函数体。类继承接口,在类中对函数进行重写,但是不需要abstract关键字

调用

与abstract类似,先声明,不能有函数体。后重写再调用。但是

区别1:

interface可以多继承,如上图它就继承了Cacualte_1,和Cacualte_2.假如还有多个,可以继续往后面加用逗号隔开。

abstract只能单继承

区别2

抽象方法abstract除了可以写抽象方法,还可以写虚方法virtual和普通方法的实现体

Interface不能写任何方法的实现体

接口若继承IDisposable,则在对象调用完后如using后,则系统会自动调用Dispose函数。

注:VB.NET中类要继承接口不能用Inherit关键字,只能用Implements

Public Class SendMessageJob

Implements IJob

Public Function Execute(context As IJobExecutionContext) As Task Implements IJob.Execute

Throw New NotImplementedException()

End Function

End Class

 

使用接口/抽象方法的好处

 

class MobilePhoneUser

{

NokiaMobilePhone MyPhone;

public MobilePhoneUser(NokiaMobilePhone m

MyPhone = m;

}

public void Use()

{

MyPhone.Call();

MyPhone.Receive();

}

 

}

class NokiaMobilePhone

{

public void Call()

{

Console.WriteLine("NokiaCall...");

}

public void Receive()

{

Console.WriteLine("NokiaReceive...");

}

}

class VivoMobilePhone

{

public void Call()

{

Console.WriteLine("VivoCall...");

}

public void Receive()

{

Console.WriteLine("VivoReceive...");

}

}

1、上面是三个类,一个时电话使用者类(MobilePhoneUser)A负责写,另外两个时电话类(B负责写),主函数使用时如下。

NokiaMobilePhone NokiaPhone = new NokiaMobilePhone();

MobilePhoneUser hh = new MobilePhoneUser(NokiaPhone);

hh.Use();

2、这样使用的局限性

    (1)、主函数调用MobilePhoneUser时必须传入NokiaMobilePhone,也就是说写MobilePhoneUser类代码的人A必须要等写

NokiaMobilePhone代码的人B写完才能继续。因为B不写完,A就不能New以及A不知道B中的函数名是什么,根本

无法调用B中的任何函数

(2)、假如用户类MobilePhoneUser需要更换手机,不用NokiaMobilePhone,而是用VivoMobilePhone这时不仅

仅需要B多增加一个VivoMobilePhone类,同时A也要修改代码,主函数调用也需要修改,如下标注红色的是修改部

分:

class MobilePhoneUser

{

VivoMobilePhone MyPhone;

public MobilePhoneUser(VivoMobilePhone m) {

MyPhone = m;

}

public void Use()

{

MyPhone.Call();

MyPhone.Receive();

}

}

VivoMobilePhone NokiaPhone = new VivoMobilePhone();

MobilePhoneUser hh = new MobilePhoneUser(NokiaPhone);

hh.Use();

3、改进方法:在开始做项目钱,A与B讨论出一个契约(接口/抽象方法),A中直接使用接口,B中继承接口。有了这份契约这样两方就可以同时工作,不用等B完成A才能开始,接口如下:

public interface IMobilePhone

{

void Call();

void Receive();

}

4、A拿到上面讨论出来的契约后(接口/抽象方法),就可以开始工作写MobilePhoneUser类:

class MobilePhoneUser

{

IMobilePhone MyPhone;

public MobilePhoneUser(IMobilePhone m) {

MyPhone = m;

}

public void Use()

{

MyPhone.Call();

MyPhone.Receive();

}

}

5、B拿到上面讨论出来的契约后(接口/抽象方法),就可以开始工作写NokiaMobilePhone、VivoMobilePhone类:

class NokiaMobilePhone: IMobilePhone

{

public void Call()

{

Console.WriteLine("NokiaCall...");

}

public void Receive()

{

Console.WriteLine("NokiaReceive...");

}

}

 

class VivoMobilePhone : IMobilePhone

{

public void Call()

{

Console.WriteLine("VivoCall...");

}

public void Receive()

{

Console.WriteLine("VivoReceive...");

}

}

6、调用方也可以开始写代码了:

IMobilePhone MyPhone= new NokiaMobilePhone();

MobilePhoneUser hh = new MobilePhoneUser(MyPhone);

hh.Use();

7、假如换手机,只要更改一个地方,后面假如学了反射,只需要改写配置即可,代码都不用修改:

IMobilePhone MyPhone = new VivoMobilePhone();

MobilePhoneUser hh = new MobilePhoneUser(MyPhone);

hh.Use();

 

总结:上面引入了接口后,A与B开始项目前就规定了接口的函数名称以及参数,他们就可以同时开始工作了,即利用接口的"父类的类型接收子类对象"特性。更换手机也变得方便了。这就是程序中的解耦合

扩展方法 this

上面的virtual,abstract、interface等都是为了修改底层的函数内容功能,扩展方法是在不修改源码的情况下新增一些功能即向现有的类添加方法,不需要创建新的派生类型。

经过上述添加string类的扩展方法Function后,后面所有string对象都有这个function方法了

 

 

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

 

namespace ConsoleApp1

{

class Program

{

static void Main(string[] args)

{

using (InterfaceClass InterfaceClass = new InterfaceClass()) {

int num2 = InterfaceClass.Add(2, 3);

Console.WriteLine($"2+3={num2}");

} ;

Console.ReadKey();

}

}

public interface Cacualte_2

{

int Sub(int a, int b);

}

public interface Cacualte_1

{

int Add(int a, int b);

}

public class InterfaceClass : Cacualte_1, Cacualte_2,IDisposable

{

public int Add(int a, int b)

{

return a + b;

}

 

public int Sub(int a, int b)

{

return a - b;

}

public void Dispose()

{

Console.WriteLine("释放所有资源");

}

}

 

 

}

posted @ 2022-04-30 18:39  ihh2021  阅读(69)  评论(0)    收藏  举报