C#基础——面向对象(封装 继承 多态)

发布时间:2023年12月20日

C# 属于面向对象编程,所谓"万物皆对象"。

面向对象编程的三大特性:封装、继承、多态。

1、封装:封装是一种将数据和方法包含在类中的机制,以避免外部代码直接访问和修改类的内部数据。可以使用访问修饰符(public、private、protected和 internal 等)来控制数据和方法的可见性和访问级别。

//类能够较好的体现封装的特点
public class Person
{
    private string name;
    private int age;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public int Age
    {
        get { return age; }
        set { age = value; }
    }

    public void SayHello()
    {
        Console.WriteLine("Hello, my name is " + name + " and I am " + age + " years old.");
    }
}

// 使用封装后的类
Person person = new Person();
person.Name = "郭贝贝同学";
person.Age = 18;
person.SayHello(); // 输出:Hello, my name is 郭贝贝同学 and I am 18 years old.

2、继承:继承是一种从已有类派生新类的机制,新类继承了已有类的特征和行为,并且可以增加或修改新类的特征和行为。C#中支持单层继承和多层继承。

Object 属于基类:所有引用类型的基础类。Object没有有特点的属性和方法,只是为所有的类提供了一个基础。例如:Array类,继承自Object,叫做Object的派生类,他们之间的关系构成了继承关系

//创建一个People类
//假设:People属于基类(父类),提供了人类需要具备的属性和行为。
internal class People {
  public int Age { get; set; }
  public string Name { get; set; }
  public float Height { get; set; }

  //私有属性
  private decimal Money = 10000000;

  //声明构造函数
  public People() {
    Console.WriteLine("People类的构造函数");
  }
    
  //重构一个构造函数
  public People(int age, string name, float height) {
    Console.WriteLine("People类的构造函数222");
    this.Age = age;
    this.Name = name;
    this.Height = height;
  }
   
  //方法
  public void Speak() {
    //如果继承的时候该方法被子类调用,this指的是子类对象。
    Console.WriteLine("我叫{0}", this.Name);
  }
}
//当 :People 加上去之后,就表示当前Man类属于People类的子类(派生类),具备了继承的特性
internal class Man : People {
  // 子类除了可能继承父类的属性以外,也可以拓展属于自己的属性
  public string huzi;
    
  //子类也可以定义私有方法
  public void drink(string tea) {
    Console.WriteLine("{0}正在悠哉的喝着{1}", Name, tea);
  }
    
  //子类的构造函数
  public Man() {
    Console.WriteLine("这是Man的构造函数");
  }
    
  //构造函数重构
  public Man(int age, string name, float height) {
    //隐式执行了 new People();
    Name = name;
    Age = age;
    Height = height;
  }
}

 Man superMan = new Man();
 //即使Man中并没有声明Name属性,但是因为是继承自People类的所以可以直接使用
 superMan.Name = "郭贝贝";
 // huzi就属于自己的属性,子类的属性只允许自身访问,父类People是没办法访问的。
 superMan.huzi = "关公胡";
 //方法和属性一样,只要公开了,都会继承过来
 superMan.Speak(); //注意此处的Speak方法是基类的,在这里也可以调用 
 superMan.drink("毛尖茶");

 //使用子类自身的构造函数
 Man man = new Man(19, "小郭", 1.82f);
 Console.WriteLine($"我叫{man.Name},今年{man.Age}岁,我的身高是{man.Height}米。");

//父类中的私有属性是不能被继承的
//superman.Money = 10000; //报错

以上代码示例只是单层继承

下面示例是在上面代码基础上实现多层继承

//在上面代码中我们可以看到继承关系是 Man : People
internal class Women : Man { } //让Women 继承 Man
internal class Son : Women { } //让Son 继承 Women

Son ss = new Son();
ss.Name = "派生类的派生类"; 
Console.WriteLine(ss.Name); //儿子
ss.Speak(); //我叫派生类的派生类
//我们可以看到嵌套多层继承仍然可以实现

base 和 this 关键字

this和base都是用于引用对象的关键字。

this关键字表示当前对象的引用。在类的方法中使用this关键字可以明确指定成员变量或成员方法是属于当前对象的。在构造函数中使用this关键字可以调用同一个类的其他构造函数。

class MyClass
{
    private int x;

    public void SetX(int x)
    {
        this.x = x; // 使用this关键字明确指定成员变量x属于当前对象
    }

    public int GetX()
    {
        return this.x; // 使用this关键字明确指定成员变量x属于当前对象
    }
}

base关键字用于引用基类的成员。在派生类中使用base关键字可以调用基类的构造函数或成员方法。

class MyBaseClass
{
    protected int x;

    public MyBaseClass(int x)
    {
        this.x = x;
    }

    public void PrintX()
    {
        Console.WriteLine("x = " + this.x);
    }
}

class MyDerivedClass : MyBaseClass
{
    public MyDerivedClass(int x) : base(x)
    {
        // 调用基类的构造函数
    }

    public void PrintAll()
    {
        base.PrintX(); // 调用基类的成员方法
        Console.WriteLine("This is a derived class.");
    }
}

多态:同一个方法名可以在不同的对象上具有不同的行为。

多态性的实现主要依靠以下两个重要的概念:继承和方法重写。

继承(Inheritance):继承是面向对象编程的基本特性之一,它允许一个类派生出子类,并继承父类的属性和方法。子类可以重新定义(覆盖)父类的方法,也可以增加自己独有的方法。通过继承,可以创建一个通用的父类,并在子类中根据需要进行特定的实现。

方法重写(Method Overriding):方法重写是指在子类中重新定义与父类中具有相同名称和参数列表的方法。子类可以根据自身的需求重新实现该方法,从而改变方法的行为。通过方法重写,可以在不同的对象上调用相同的方法名,但实际执行的是各自类中的具体实现。

 internal class People {
   public int Age { get; set; }
   public string Name { get; set; }
   public void Eat() {
     Console.WriteLine("吃饱了");
   }
   // virtual:表示该方法是一个虚方法,这个方法其实我们在基类(父类)中只需要声明,将来要在派生类(子类)中实现。相当于在父类中定义,在子类中重写
   public virtual void Drink() {
     Console.WriteLine("喝晕了");
   }
 }

 class Man : People {
   //如果直接定义和父类中一样的方法,那么会把父类的方法覆盖掉
   public void Eat() {
     Console.WriteLine("没有吃饱");
   }
   //override 代表重写:把父类中的方法进行重新构造,不覆盖父类的方法。
   public override void Drink() {
     //如果想保留父类的方法 使用base调用父类的方法
     base.Drink();
     Console.WriteLine("我没喝多,再来一瓶");
   }
   public override string ToString() {
     Console.WriteLine("重写的是Object的方法");
     return base.ToString();
   }
 }

 Man man = new Man();
 //覆盖父类的方法
 man.Eat();

 //重写的方法
 man.Drink();
 man.Age = 20;
 //相当与在原有的ToString基础上拓展业务逻辑
 man.ToString();

重载与重写

重载:重载指的是在同一个类中定义多个具有相同名称但参数列表不同的方法或函数。重载方法可以根据不同的参数类型、参数数量或参数顺序来执行不同的操作。

public class Calculation {
    //定义一个方法
    public int sum(int num1, int num2) {
        return num1 + num2;
    }

    //定义一个名称相同,但参数列表不同的方法
    public float sum(float num1, float num2) {
        return num1 + num2;
    }
    
 	//再次定义一个名称相同,但参数列表不同的方法
    public int sum(int num1, int num2, int num3) {
        return num1 + num2 + num3;
    }
}

重写:重写指的是在派生类中重新定义基类中已有的方法,以实现特定的行为。重写方法的名称、返回类型和参数列表必须与基类中的方法完全相同。

public class People{
	public virtual void Drink() {
    	 Console.WriteLine("我是基类的方法");
	}
}

public class Man : People{
     public override void Drink() {
     	Console.WriteLine("我是派生类的方法");
 	}
 }

重写的必要条件:

  • 发生在继承关系中,派生类重写基类的方法。
  • 修饰符,方法名,返回值类型都需要一致。
  • 重写时,基类要有使派生类重写的修饰符,而派生类也要有重写修饰符override
  • 重写后,派生类的方法就覆盖了基类的方法,如果想保留基类方法,使用base调用

重写与重载的区别:

  • 相同点:方法名字必须相同,访问修饰符相同
  • 不同点:
    • 重载:在一个类中,并且参数列表不同,返回值无需要求,不用使用关键字就可以重载。
    • 重写:不在一个类中,是继承关系,参数列表相同,返回值类型不同,需要使用关键字。
文章来源:https://blog.csdn.net/qq_51810680/article/details/135035050
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。