我的C#学习笔记(一)面向对象

作者 Zhendong Ho 日期 2018-08-27
C#
我的C#学习笔记(一)面向对象

C#字符串

StringBuilder和String的区别

String在进行运算时(如赋值、拼接等)会产生一个新的实例,而StringBuilder则不会。

字符串的不可变性

  1. 当你给一个字符串重新赋值之后,老的值并没有销毁,而是重新开辟一块空间存储新值。
  2. 当程序结束后,GC扫描整个内存,如果发现有空间没有被指向,则立即把它销毁。

字符串是一个char类型的一个只读数组

  1. ToCharArray();将字符串转换为char数组。
  2. new string(char[] chs);能够将char数组转换为字符串。

字符串提供的各种方法

  1. Length,获得当前字符串中字符的个数。
  2. ToUpper(),将字符串转换成大写形式。
  3. ToLower(),将字符串转换成小写形式。
  4. Equal(str,Stringcomparison.OrdinalIgnoreCase),比较两个字符串的值是否相等,参数可忽略大小写。
  5. Split(),分割字符串,返回字符串类型的数组。
  6. Substring(),截取字符串,在截取的时候包含要截取的字符的位置。
  7. IndexOf(),判断某个字符串在字符串中第一次出现的位置,如果没有返回-1。
  8. LastIndexOf(),判断某个字符串在字符串中最后一次出现的位置,如果没有返回-1。
  9. StartsWith(),判断以…开始。
  10. EndsWith(),判断以…结束。
  11. Replace(),将字符串中某个字符串替换成一个新的字符串。
  12. Contains(),判断某个字符串是否包含指定的字符串。
  13. Trim(),去掉字符串中前后的空格。
  14. TrimEnd(),去掉字符串中结尾的空格。
  15. TrimStart(),去掉字符串中前面的空格。
  16. string.IsNullOrEmpty(),判断一个字符串是否为空或者为null。
  17. string.Join(),将数组按照指定的字符串连接,返回一个字符串。

继承

我们可能会在一些类中,写一些重复的成员,我们可以将这些重复的成员,单独封装到一个类中,作为这些类的父类。如Student、Teacher、Driver称为子类或派生类,Person称为父类或基类。

继承的特性

  1. 继承的单根性:一个子类只能有一个父类,即单继承。
  2. 继承的传递性:如Student继承自Person,Programmer继承自Student,Driver继承自Programmer,则最后的类Driver既可以访问Programmer类中的属性和方法,也可以访问Person类中的属性和方法。

查看类图

VS2017中,需要安装VS扩展开发,并勾上类设计器后,在项目-右键-查看类图。

问题:子类有没有继承父类的构造函数?

答:子类并没有继承父类的构造函数。但是,子类会默认调用父类无参数的构造函数。在子类的内部创建父类的对象,让子类可以使用父类中的成员。

  • 如果在父类中重写了一个有参数的构造函数之后,那个默认的无参数构造函数就被覆盖掉了,子类调用不到,因此会报错。

  • 解决办法:

    1. 在父类中重新写一个无参数的构造函数。
    2. 在子类中显式调用父类的有参数的构造函数,使用关键字:base()

    如:

    public Student(string name, string age, string gender, string id) : base(name, age, gender)
    {
    this.id = id;
    }

new关键字

  1. 创建对象。

  2. 隐藏从父类那里继承过来的同名成员。

    隐藏的后果就是子类调用不到父类的成员。

里氏转换法

里氏转换

  1. 子类可以赋值给父类:如果有一个地方需要一个父类作为参数,我们可以给一个子类代替。

    如:

    Student s = new Student();
    Person p = s;

    或者

    string str = string.Join("|", new string[] { "1", "2", "3", "4"});

    Join方法第二个参数为object,可以用一个子类代替,string为object的一个子类。

  2. 如果父类中装的是子类对象,那么可以将这个父类强转为子类对象。

    如:

    Person p = new Student();
    Student ss = (Student)p;

注意:子类对象可以调用父类中的成员,但是父类对象永远都只能调用自己的成员。

as和is关键字

is表示类型转换,如果能够转换成功,则返回一个true,否则返回一个false。

as表示类型转换,如果能够转换则返回对应对象,否则返回一个null。

is的用法:

if (p is Student)
{
Student ss = (Student)p;
ss.StudentSayHello();
}
else
{
Console.WriteLine("转换失败");
}

as的用法:

Student t = p as Studnet;

Protected

受保护的:可以在当前类的内部以及该类的子类中访问。

多态

概念

让一个对象能够表现出多种的状态(类型)。

多态的好处

  1. 减少冗余的代码
  2. 增加程序的可扩展性
  3. 取消各个对象的差异性

实现多态的三种方法

虚方法

将父类的方法标记为虚方法,使用关键字virtual,这个方法可以被子类用关键字override重新写一遍。

解释:emps[i].DaKa()调用的依然是父类的方法,只是该方法被子类重写了,调用的时候不再执行父类的方法,而是执行子类重写的方法,调用哪个子类的重写方法取决于对象里面装的是哪个子类。

//员工类
public virtual class Employee
{
public void DaKa()
{
Console.WriteLine("9点打卡");
}
}

//经理类
public class Manager : Employee
{
public override void DaKa()
{
Console.WriteLine("经理11点打卡");
}
}

//程序猿类
public class Programmer : Employee
{
public override void DaKa()
{
Console.WriteLine("程序猿不打卡");
}
}

Main方法

//经理11点打卡,员工9点打卡,程序猿不打卡
Employee emp = new Employee();
Manager mg = new Manager();
Programmer pm = new Programmer();
Employee[] emps = { emp, mg, pm };

for (int i = 0; i < emps.Length; i++)
{
emps[i].DaKa();
}
Console.ReadKey();

抽象类

当父类中的方法不知道如何去实现的时候,可以考虑将父类写成抽象类,将方法写成抽象方法。

抽象类的特点:

  1. 抽象类和接口不允许创建对象。
  2. 抽象成员必须标记为abstract,并且不能有任何实现。
  3. 抽象类中可以有非抽象成员,并且非抽象成员在子类中不需要被重写。
  4. 抽象类成员必须在抽象类中。
  5. 子类继承抽象类后,必须把父类中的所有抽象成员都重写。(除非子类也是一个抽象类,则可以不重写)
  6. 抽象类是有构造函数的,虽然不能被实例化。
  7. 如果父类的抽象方法中有参数,那么继承这个抽象父类的子类在重写父类的方法的时候,必须传入对应的参数。如果抽象父类的抽象方法中有返回值,那么子类在重写这个抽象方法的时候,也必须要有相同的返回值。

注意:方法的签名就是方法的返回值还有参数。

//抽象类
public abstract class Animal
{
public abstract void Bark();
}

//狗类
public class Dog : Animal
{
public override void Bark()
{
Console.WriteLine("狗狗旺旺的叫");
}
}

//猫类
public class Cat : Animal
{
public override void Bark()
{
Console.WriteLine("猫咪喵喵的叫");
}
}

Main方法

Animal a = new Dog();
a.Bark();//狗狗旺旺的叫
Console.ReadKey();

虚方法抽象类总结

  • 如果父类中的方法有默认的实现,并且父类需要被实例化,这时可以考虑把父类定义成一个普通类,用虚方法来实现多态。
  • 如果父类中的方法没有默认实现,父类也不需要被实例化,则可以将该类定义为抽象类。
  • 在抽象类中写一个虚方法,是有意义的。(如果类中需要抽象成员,就写抽象类。如果不需要抽象成员,则不写抽象类)