类Class与对象Object

  • 是一种数据结构
  • 是一种数据类型
  • 代表现实中的“种类”

是对一切事物的描述,是抽象的,概念上的定义 对象是实际存在的该类事物的每个个体,因而也称为实例 万物皆对象

面向对象特征

  • 封装性
  • 继承性
  • 多态性(抽象性)

    修饰符

    访问修饰符

    public

    公有的,任何代码均可以访问,应用于所有类或成员

    internal

    内部的,同一个程序集的对象可以访问,就是当前项目里面可以访问;不写默认就是它

    protected

    受保护的,该类内部和继承类中可以访问。

    private

    只有同一个类中的函数可以访问它的私有成员。

    protected internal

    protected和internal的并集,符合任意一条都可以访问

static

静态的,表示这个属性,方法等是属于这个类的而不是对象个体。静态类不能实例化对象

  1. public static class Test
  2. {
  3. public static string Id { get; set; }
  4. public static void Show()
  5. {
  6. Console.WriteLine("静态方法执行");
  7. }
  8. }

virtual

虚方法,写在父类的方法上,子类可以选择对这个方法就行重写。详情看本文的重写部分

override

重写方法,重写父类的virtual虚方法和abstract抽象方法。详情看本文的重写部分

abstract

随着继承越来越多,类变得越来越具体。而父类应该是一个抽象的概念,不应该有实体。

子类继承抽象基类,如果基类有抽象方法,子类不实现这个抽象方法,必然子类也是一个抽象类

  1. namespace ConsoleApp2
  2. {
  3. //动物应该是一个抽象的概率,设置抽象类就不能实例化对象了
  4. //抽象类里面,可以正常写属性方法
  5. //如果这个类里面的属性方法加上abstract,那么这个类也必须加abstract
  6. abstract public class Animal
  7. {
  8. public string Type { get; set; }
  9. public Animal(string type)
  10. {
  11. this.Type = type;
  12. }
  13. //基类里面要被重写的方法加上virtual,虚方法。
  14. public virtual void Sleep()
  15. {
  16. Console.WriteLine("动物都要睡觉");
  17. }
  18. //一个抽象方法,也可以叫纯虚方法,虚无缥缈到方法体都没有了
  19. //子类要继承这个类,必须重写抽象方法。virtual不是必须的
  20. //多态的概念
  21. //因为不知道不同的动物都吃什么,就让不同的动物子类去实现他们自己吃啥。但是作为动物,是必须要吃东西的,基类定义了一个概念,子类遵守概念拓展
  22. public abstract void Eat();
  23. }
  24. public class Cat : Animal
  25. {
  26. public Cat(string type):base(type)
  27. {
  28. }
  29. //子类重写父类的抽象方法,还是用override
  30. public override void Eat()
  31. {
  32. Console.WriteLine("猫吃鱼");
  33. }
  34. //子类里面重写基类方法家还是那个override,重写方法
  35. public override void Sleep()
  36. {
  37. Console.WriteLine("猫在猫窝里面睡觉");
  38. }
  39. }
  40. public class Dog : Animal
  41. {
  42. public Dog(string type):base(type)
  43. {
  44. }
  45. //子类重写父类的抽象方法
  46. public override void Eat()
  47. {
  48. Console.WriteLine("狗吃肉");
  49. }
  50. public override void Sleep()
  51. {
  52. Console.WriteLine("狗在狗窝睡觉");
  53. }
  54. }
  55. }
  1. namespace ConsoleApp2
  2. {
  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  7. Animal animal1 = new Cat("猫");
  8. animal1.Eat();//猫吃鱼
  9. }
  10. }
  11. }

如果一个抽象类里面全是抽象的东西,和接口就很像了

sealed

密封

  1. abstract class A
  2. {
  3. public abstract string Name { get; set; }
  4. public abstract void a();
  5. public abstract void b();
  6. }
  7. class B : A
  8. {
  9. //子类不能重写这个属性了
  10. public sealed override string Name { get; set; }
  11. public override void a()
  12. {
  13. }
  14. //继承的子类无法对这个方法进行重写了,而且sealed只能用在重写的方法上面,和override一起用
  15. public sealed override void b()
  16. {
  17. }
  18. }
  19. //这个类被密封了,不能被其他子类继承
  20. sealed class C : B
  21. {
  22. public override void a()
  23. {
  24. }
  25. }

partial

一个类太长,拆分成多个类分别写。只是分别写,其实还是同一个类。举个例子,有时候系统自动生成的类的代码,给他加上partial,我自己重新写一个这个类,加上partial,在同一个命名空间下。
这就实现了,我写的部分,和系统自动生成的部分分离。

  1. // partial1.cs:
  2. namespace CodeSamples
  3. {
  4. public partial class MyPartialClass
  5. {
  6. public int method1(int x)
  7. {
  8. Console.WriteLine(x);
  9. return 0;
  10. }
  11. }
  12. }
  13. // partial2.cs
  14. namespace CodeSamples
  15. {
  16. public partial class MyPartialClass
  17. {
  18. public void method2(double x)
  19. {
  20. Console.WriteLine(x);
  21. }
  22. }
  23. }
  24. // program.cs
  25. static void Main(string[] args)
  26. {
  27. MyPartialClass obj = new MyPartialClass();
  28. obj.method1(11);
  29. obj.method2(34.23);
  30. Console.Read();
  31. }

封装属性

快捷操作,点击私有字段名,Ctrl+R+E

  1. class Person
  2. {
  3. private string name;
  4. private int age;
  5. //通过写getset方法来进行封装字段
  6. public string getName()
  7. {
  8. return this.name;
  9. }
  10. public void setName(string name)
  11. {
  12. this.name = name;
  13. }
  14. }
  1. class Person
  2. {
  3. private string name;
  4. public string Name
  5. {
  6. get
  7. {
  8. return this.name;
  9. }
  10. set
  11. {
  12. this.name = value;//用属性的方式,set里面自带的value就是给Name属性赋的值
  13. }
  14. }
  15. private int age;
  16. }

自动属性是C# 5.0(含)之后,微软新增的语法糖,全称为 Auto-Implemented Properties。如果属性的set和get访问器中没有任何逻辑,就可以使用自动实现的属性。不需要声明私有化字段。编译器会自动创建它。

  1. class Person
  2. {
  3. public string Name { get; set; }
  4. //可以设置getset访问修饰符,还可以赋初始值
  5. public int Age { private get; set; } = 42;
  6. }

可以只写get只读属性,只写set只写属性。如果一个属性需要动态的计算出来,可以使用getset实现“计算属性”的功能

析构器

手动释放类对象资源,当类的对象用完了,没有引用的时候,就会执行析构器,释放这个对象资源

  1. public class Animal
  2. {
  3. ~Animal(){
  4. //对象没有引用时,就执行了这个析构器,释放了资源
  5. }
  6. }

继承

减少代码冗余,提高复用性。
子类继承父类,子类就拥有了父类的全部属性和方法,便于功能扩展
继承是多态的前提。
所有类都继承自Object类
子类的访问级别不能超过父类,例如,父类是internal,子类就不能是public。

base访问父类的属性,方法,构造器等

  1. public class Animal
  2. {
  3. public string Type { get; set; }
  4. public Animal(string type)
  5. {
  6. this.Type = type;
  7. }
  8. }
  9. //Cat继承了Animal,拥有了Animal的Type属性
  10. public class Cat : Animal
  11. {
  12. //当实例化子类时,会先调用父类的构造器。
  13. //默认就调用空参构造器,如果基类构造器重载了,而没有显式的写空参构造器
  14. //有两种写法①,base里面传入和基类构造器匹配的参数
  15. public Cat():base("动物")
  16. {
  17. this.Type = "猫";
  18. }
  19. //②
  20. public Cat(string type):base(type)
  21. {
  22. }
  23. public void ShowType()
  24. {
  25. //base关键字访问父类,只能向上访问一层
  26. //但是这里base和this一样,因为Cat继承了Animal的Type属性
  27. Console.WriteLine(base.Type);
  28. //this就是指向当前类
  29. Console.WriteLine(this.Type);
  30. }
  31. }

重写

注意:重写(override)和重载(overload)不一样

重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作 重载:同名,不同参的方法复写,参数可以个数不同,按顺序类型不同。调用时根据传入的参数决定调用哪个方法

在C#中如果不加virtual和override,那是隐藏,不是重写。子类继承了父类的方法A,子类本身又声明了一个一模一样的方法A(方法体不一样)。执行子类的方法A会直接调用父类的方法A,子类写的被隐藏了。

  1. public class Animal
  2. {
  3. public string Type { get; set; }
  4. public Animal(string type)
  5. {
  6. this.Type = type;
  7. }
  8. //基类里面要被重写的方法加上virtual,虚方法
  9. public virtual void Sleep()
  10. {
  11. Console.WriteLine("动物都要睡觉");
  12. }
  13. }
  14. public class Cat : Animal
  15. {
  16. public Cat():base("动物")
  17. {
  18. this.Type = "猫";
  19. }
  20. //子类里面重写基类方法家还是那个override,重写方法
  21. public override void Sleep()
  22. {
  23. Console.WriteLine("猫在猫窝里面睡觉");
  24. }
  25. }
  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. //虚方法和重写方法都能直接调用
  6. Cat cat = new Cat();
  7. cat.Sleep();//猫在猫窝里面睡觉
  8. Animal animal = new Animal("动物");
  9. animal.Sleep();//动物都要睡觉
  10. }
  11. }

多态

一个事物的多种形态。
现在有动物类,和各种动物的子类

  1. public class Animal
  2. {
  3. public string Type { get; set; }
  4. public Animal(string type)
  5. {
  6. this.Type = type;
  7. }
  8. //基类里面要被重写的方法加上virtual,虚方法
  9. public virtual void Sleep()
  10. {
  11. Console.WriteLine("动物都要睡觉");
  12. }
  13. }
  14. public class Cat : Animal
  15. {
  16. public Cat(string type):base(type)
  17. {
  18. }
  19. //子类里面重写基类方法家还是那个override,重写方法
  20. public override void Sleep()
  21. {
  22. Console.WriteLine("猫在猫窝里面睡觉");
  23. }
  24. }
  25. public class Dog : Animal
  26. {
  27. public Dog(string type):base(type)
  28. {
  29. }
  30. public override void Sleep()
  31. {
  32. Console.WriteLine("狗在狗窝睡觉");
  33. }
  34. }

我要一只动物。
只要是动物就行,要求不高,什么都可以,可以是猫,可以是狗。毕竟我只是要一只动物,猫狗都是动物,符合要求。猫狗都是动物,也就是子类,不可能我要动物,你给我个变形金刚吧,和动物不是一类啊。

动物只是一种统称,动物都是有生命的,都要睡觉,吃饭。但是不同的动物生命的形态不同,吃的东西不同,睡觉的方式不同。
这就是多态。

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Animal animal1 = new Cat("猫");
  6. Animal animal2 = new Dog("狗");
  7. animal1.Sleep();//猫在猫窝里面睡觉
  8. animal2.Sleep();//狗在狗窝睡觉
  9. }
  10. }

为什么要用多态

一个方法,要传入一个参数。这个参数什么类型都可以。
如果我声明参数为int类型,要是传入string怎么办。要是声明参数类型为double类型,而我要传入自己写的类对象怎么搞。
我直接声明参数类型为Object,所有类的基类,想传什么类型都可以,反正都是他儿子们。
接口同理,接口和基类是兄弟,同级。

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. T(123);//这是一个System.Int32
  6. T("这是字符串");//这是一个System.String
  7. T(new int[10]);//这是一个System.Int32[]
  8. T(new Cat("猫"));//这是一个ConsoleApp2.Cat
  9. }
  10. static void T(Object obj)
  11. {
  12. Console.WriteLine($"这是一个{obj.GetType()}");
  13. }
  14. }