继承

面向对象编程的三大特性**(封装、继承、多态)**

继承 允许我们基于现有类创建新类,实现代码重用

继承的特点

  • 单继承:C# 只支持单继承,一个类只能直接继承一个父类
  • 传递性:继承关系可以多层传递
  • 所有类都继承自 Object:即使没有显式指定,所有类都隐式继承自 System.Objec
  • //继承:主要是为了减少代码的复用,如果有一个类的属性和方法想在另外一个类进行使用的话,可以通过继承实现属性和方法的复用
    //a类继承b类,b类叫基类或者父类,a叫子类或者派生类
    //继承通过冒号实现继承,c#只能是单继承,也就是a继承b,b继承c,但是不能让a同于继承b和继承c
    //继承不写冒号,默认是继承object,object类叫最终基类
    static void Main(string[] args)
    {
        Student s1 = new Student();
        s1.Name = "Test";
        s1.Address = "s";
        s1.F1();

        People p1 = new People();
    }
}

//定义父类
public class People
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    private int Age = 10;//私有的不能被继承,
    protected int CC { get; set; }//受保护的属性 People的实例不能访问,子类可以使用
    public void F1()
    {
        Console.WriteLine("父类的F1方法");
    }
}
//学生类 继承于People
public class Student:People
{
    public void F2()
    {
        Console.WriteLine(Name +":"+Id+":"+CC);
    }
}

子类构造和父类构造执行顺序

    static void Main(string[] args)
    {
        //People p1 = new People();
        //People p2 = new People("福瑞");

        //在子类继承父类的时候,父类必须先创建,创建子类的对象,会先调用父类的无参数的构造函数
        //student s1 = new student();
        //即使子类调用的是带参数的构造函数,默认情况下还是先调用父类的无参数构造函数
        //如果想调用父类带参数的构造函数  在子类有参数构造函数后面添加 :base("11")调用父类有参数构造函数
        Student s2 = new Student(12,"张三");
    }
}
public class People
{
    public string Name { get; set; }
    public People ()
    {
        Console.WriteLine("1 父类无参数的构造函数");
    }
    public People(string name)
    {
        Console.WriteLine("2 父类带参数的构造函数");
    }
}
public class Student:People
{
    public int Age { get; set; }
    public Student ():base()//base() base指的是父类的一个对象, :base()调用父类的构造函数
    {
        Console.WriteLine("3 子类无参数的构造函数");
    }
    public Student(int age,string n):base(n)//:base(n) 调用父类的有参数的构造函数 
    {
        Console.WriteLine("4 子类带参数的构造函数"+Name);
    }
}

属性访问修饰符

internal class Program
{
    static void Main(string[] args)
    {
        //1.内部能不能调用 2.在外部使用实例去调用 3.子类(其他项目)能不能使用


        //public 公共的 当前类 子类 实例 都可以访问,其他项目如果也继承了类,其他项目的子类也可以访问
        //private 私有的 仅能在当前类内部访问 子类、实例都不能访问
        //protected 受保护的 当前类以及子类(包括引用了当前项目的子类)
        //internal 当前类 子类 实例 都可以访问  其他项目里都不能访问
        //protected internal 能在当前类

        People p = new People();
        Console.WriteLine(p.a); //公共的 在外部可以通过实例访问
        //Console.WriteLine(p.b); //不能被实例访问
        //Console.WriteLine(p.c); //不能被实例访问
        Console.WriteLine(p.d); //protected internal 对象可以访问
        Console.WriteLine(p.e); //对象可以访问
    }
}
public class People
{
    public int a = 10;
    private int b = 20;
    protected int c = 30;
    protected internal int d = 50;
    internal int e = 40;
    public void F1()
    {
        Console.WriteLine(a); //公共的 在内部可以访问
        Console.WriteLine(b); //私有可以访问
        Console.WriteLine(c); //可以访问受保护的
        Console.WriteLine(d); //可以访问protected internal
        Console.WriteLine(e); //可以访问internal
    }
}
public class Man:People
{
    public void F2()
    {
        Console.WriteLine(a); //公共的 在子类可以访问
        //Console.WriteLine(b); //私有的 不能被子类访问
        Console.WriteLine(c); //可以访问受保护的
        Console.WriteLine(d); //可以访问protected internal
        Console.WriteLine(e); //子类可以访问internal
    }
}
访问修饰符/范围 当前类 子类 实例对象 引用当前项目的项目子类 引用当前项目实例对象
public
private × × × ×
internal × ×
protected 不能 不能
protected internal 不能

多态

多态:同一个行为具有不同的表现形式和能力

多态的分类

  • 编译时多态(静态多态):通过方法重载(Method Overloading)和运算符重载(Operator Overloading)来实现。
  • 运行时多态(动态多态):通过继承和接口实现,以及方法重写(Method Overriding)来实现。

静态多态:在编译过程中,通过方法重载和运算符重载来实现,也称之为静态绑定和早期绑定

动态多态:在运行过程中,通过方法重写,隐藏方法来实现,也称之为动态绑定或者后期绑定

函数重载

    static void Main(string[] args)
    {
        //多态:同一个功能 不同的一个体现
        //静态多态:函数重载和符号重载
        //动态多态:通过继承接口 或者继承抽象类型实现动态多态

        //能够体现多态的地方:函数重载
        //函数重载满足条件:
        //1.函数名必须一样
        //2.参数的个数 类型不一样
        //3.仅仅只有返回值类型不一样 不是重载 


    }
    public static void F1()
    {
        Console.WriteLine("F1不带参数");
    }
    public static void F1(int a)
    {
        Console.WriteLine("F1带一个参数");
    }
    public static void F1(string a)
    {
        Console.WriteLine("F1带一个参数");
    }
    public static void F1(string a,int b)
    {
        Console.WriteLine("F1带两个参数");
    }
    public static void F1(int b,string a)
    {
        Console.WriteLine("F1带两个参数");
    }
    public static int F1(bool c)
    {
        Console.WriteLine("F1带1个参数");
        return 10;
    }
}

运算符重载

可重载与不可重载的运算符

运算符 可重载性
+、-、!、++、– 这些一元运算符可以进行重载
<、>、=、!=、<=、>= 这些二元运算符可以进行重载,需要注意的是某些运算符必须成对重载
&&、|| 无法重载逻辑运算符
(type)var_name 强制类型转换运算符不能重载
+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>= 复合赋值运算符不能显式重载。 但在重载二元运算符时,也会隐式重载相应的复合赋值运算符,例如重载了+运算符也会隐式的重载+=
^、=、.、?.、? : 、??、??=、…、->、=>、as、await、checked、unchecked、default、delegate、is、nameof、new、sizeof、stackalloc、switch、typeof 这些运算符无法进行重载

注意:比较运算符必须成对重载,也就是说,如果重载一对运算符中的任意一个,则另一个运算符也必须重载。比如==!=运算符、<>运算符、<=>=运算符。

internal class Program
{
    static void Main(string[] args)
    {
        //符号重载:对运算符号或者比较符号等进行功能的重写 例如可以让两个对象能够实现相加
        Box b1 = new Box(10,20,30);
        Box b2 = new Box(30,40,50);

        Box b3 = b1 + b2;
        Console.WriteLine(b3.Length + ":" + b3.Width + ":" + b3.Height);

        int b4 = b1 - b2;
        Console.WriteLine(b4);

        Box b5 = b1++;
        Console.WriteLine(b1.Length);//11
        Console.WriteLine(b5.Length);//10

        Console.WriteLine(b1 > b2);//false
        Console.WriteLine(b1 < b2);// true  执行<符号 执行重载函数,重载函数比较了b1和b2的体积, b1.v<b2.v成立 重载结果就为true

    }
}
public class Box
{
    public int Length { get; set; }//长
    public int Width { get; set; }//宽
    public int Height { get; set; }//高
    public Box()
    {

    }
    //体积的属性
    public int Volume
    {
        get
        {
            return Length * Width * Height;
        }
    }
    public Box(int l,int w,int h)
    {
        Length = l;
        Width = w;
        Height = h;
    }

    //重载加+ 实现两个对象相加 返回一个对象,结果对象三个属性分别是两个对象属性相加和
    public static Box operator+(Box b1, Box b2)
    {
        //return b1.Length*b1.Width*b1.Height + b2.Length * b2.Width * b2.Height; 如果返回值是整型的方法 
        return new Box()
        {
            Length = b1.Length + b2.Length,
            Width = b1.Width + b2.Width,
            Height = b1.Height + b2.Height,
        };
    }
    //重载减- ,两个对象相减等于两个体积相减的差值
    public static int operator -(Box b1, Box b2)
    {
        return b1.Length * b1.Width * b1.Height - b2.Length * b2.Width * b2.Height;
    }
    //重载++ 让对象的长宽高在基础之上加一 并且返回一个对象
    public static Box operator ++(Box b1)
    {
        return new Box()
        {
            Length = b1.Length+1,
            Width = b1.Width+1,
            Height = b1.Height+1
        };
    }

    //重载大于号
    public static bool operator>(Box b1, Box b2)
    {
        return b1.Volume > b2.Volume;
    }
    //重载小于符号
    public static bool operator <(Box b1, Box b2)
    {
        return b1.Volume < b2.Volume;
    }
}

抽象类

修饰符: abstract

特点:

  1. 不能被实例化
  2. 用于被继承(抽象类存在的意义就是作为基类,让其他的类所继承)
  3. 抽象类可以包含已经实现了的方法(普通方法) 属性 字段
  4. 还可以定义抽象方法/属性,强制子类必须实现特定的方法和属性

抽象方法特点:

  1. 抽象方法不能有方法体
  2. 抽象方法只能定义在抽象类中
  3. 子类必须使用override关键字实现所有继承的抽象方法

普通类和抽象类的区别

普通方法和抽象方法的区别

internal class Program
{
    static void Main(string[] args)
    {
        //抽象类:
        //定义时候使用abstract关键字,不能实例化
        //抽象类存在的意义就是为了被继承的, 所以一般是公共的类
        //可以定义普通属性和方法 也可以定义抽象属性和抽象方法,需要在子类把抽象的属性和方法进行实现
        //抽象类可以继承自抽象类,子类如果不是抽象类,就必须重写抽象类中全部的抽象方法

        // 抽象方法和普通方法区别
        // 抽象方法只能定义在抽象类里面,普通方法可以定义在抽象类 也可以定义普通的类中
        // 抽象方法没有方法体,普通方法需要有方法体
        // 抽象方法在子类实现. 需要通过override重新抽象方法
    }
}
//定义抽象类
abstract class People
{
    public int Age { get; set; }//普通的属性
    public void F2()  // 普通方法
    {
        Console.WriteLine("F2非抽象方法");
    }

    public abstract string Name { get; set; } // 定义抽象属性
    public abstract void F1(); //抽象方法 不要加{}
}

//定义子类继承抽象类 必须实现抽象属性和抽象方法
class Student : People
{
    public override string Name { get; set; } = "ssss";

    public override void F1()
    {
        Console.WriteLine("子类的F1");
    }
}

虚方法

特点:

  1. 可以定义在普通的类中
  2. 可以有方法体

抽象方法和虚方法的区别?

  1. 抽象方法只能定义在抽象类中,虚方法可以定义在普通类中
  2. 抽象方法不可以有方法体,必须在派生类中实现,虚方法可以有方法体,可以不在派生类中实现,不实现则使用基类的方法
internal class Program
    {
        static void Main(string[] args)
        {
            
            Chinese c1 = new Chinese();
            c1.Speak();
        }
    }
    //虚方法可以定义在普通的类中 可以理解为一个类中的抽象方法,只不过这个方法可以在类中实现,也可以在派生类中重写
    class People
    {
        public string Name { get; set; }

        //给某个方法添加virtual 关键字  把这个方法修饰成虚方法(可以有方法体)
        public virtual void Speak()
        {
            Console.WriteLine("打招呼");
        }

    }
    class Chinese : People
    {
        public  override void Speak()
        {
            Console.WriteLine("吃了吗?");
        }
    }

new和override

internal class Program
    {
        static void Main(string[] args)
        {
           // new 和 override
          // override 关键字
          // 作用: 用于在派生类中重写基类中的虚方法和抽象方法
          // new 关键字
          // 作用1: 生成实例对象
          // 作用2:用于在派生类中 隐藏类中的成员


            Man m1 = new Man();
            Peope m2 = new Man();

            m1.Test1();//Man_Test1
            m2.Test1();//Peope_Test1



            m1.Test2();//Man_Test1
            m2.Test2();//Peope_Test1

            m1.Test3();//Man_Test1
            m2.Test3();//Man_Test3


            // new 和 override  都可以实现对基类成员进行 "覆盖"
            // new 是隐藏基类中的成员 基类和派生类 各持一份 互不干扰 对象是什么类型 就是用对应类型的方法
            //override 是覆盖 将会重写基类中的虚方法和抽象方法 不管类型 都只有一个
        }
    }
    class Peope
    {
        public void Test1()
        {
            Console.WriteLine("Peope_Test1");
        }
        public virtual void Test2()
        {
            Console.WriteLine("Peope_Test2");
        }
        public virtual void Test3()
        {
            Console.WriteLine("Peope_Test3");
        }
    }
    class Man : Peope
    {
        //当派生类和基类中拥有同名的成员的时候,编译器会发出一个警告: XXXX隐藏继承的成员 XXXX如果是有一隐藏 请使用new关键字
        public new   void Test1()
        {
            Console.WriteLine("Man_Test1");
        }
        public new void Test2()
        {
            Console.WriteLine("Man_Test2");
        }
        public override void Test3()
        {
            Console.WriteLine("Man_Test3");
        }
    }

密封类和静态类部分类

internal class Program
{
    static void Main(string[] args)
    {
        //Test t1 = new Test();
        // Test1 t2 = new Test1();
        //Test2 t3 = new Test2();
    


    }
}
//抽象类型   不能实例化,用来被继承的
abstract class Test
{

}
//密封类 不能被继承  能够被实例化
sealed class Test1
{

}
//静态类要求成员都是静态的, 不能被实例化
static class  Test2
{
  public static  int Age { get; set; }
     
}

//部分类:把一个分成多个类文件进行定义,编译器在编译时候会把多个文件自动合并一个类.
// C# 部分类partial class 是 C# 中的部分类,核心作用是:将一个类的代码拆分到多个文件中,编译时编译器会自动把这些文件合并成一个完整的类。
//它的关键字是 partial,必须和 class 配合使用。
// 每个文件中的类都必须用 partial 修饰
// 类名、命名空间必须完全一致
// 最终编译后,所有部分会合并为一个类
partial class Test3
{

}

更多推荐