计算机系统应用教程网站

网站首页 > 技术文章 正文

C# - 面向对象知识总结 082

btikc 2025-01-16 18:10:38 技术文章 12 ℃ 0 评论

类,实例化与对象:类是某种事物的模板,是一种抽象化的概述;对象是根据此模板实例化的一个具体事物

类使用关键字class进行定义,对象通过关键字new实例化后产生;一般一个类使用一个.cs文件,类的访问修饰符决定类与其成员在外部被访问的权限级别(暂不涉及特殊类)

//public 访问修饰符的一种,外部任何地方都可以访问此类
//class 定义类的关键字
//Myclass 类名(强制遵守命名规则,非强制符合命名规范)
//类名一般都是大写(命名规范)
public class MyClass
{
  //类的成员  
}

类的成员暂时只说:字段,属性,构造方法与普通方法

1)字段:代表的是类的最根本的性质;一般为是私有的,只有类本身能调用(在类内部使用)

//private 访问修饰符 被修饰的字段表示是私有,外部访问不到
//int 表示这个字段的数据类型
//age 字段名称

 private int age;//以分号结尾

2)属性:一般由get,set两个方法组成;属性名与字段名相同只是需要首字母大写,一般由public进行修饰;作用:控制(或约束)字段的取值与赋值

//属性类别包括:自动属性,只读属性,只写属性,可读可写属性
public int Age{get;set;}//自动属性

//普通成员变量;与属性区分开
//最好不要与属性重名
public double Height;

//只读属性:外部只能读取这个属性的值,无法进行赋值
public int Age
{  
  get { return age; }
}
//只读与只写属性不能并有,自己测试VS会给出报错信息
 public int Age
 {
   //只写属性:外部只能为此属性赋值
   //外部无法读取此属性的值
   //value 接收外部的赋值
     set { age = value; }
 }
 public int Age
 {
   //可读可写属性:外部既能赋值也能其值
  get { return age; }
  set { age = value; }
}

3)构造方法:与类名相同一般由public进行修饰;如果不写有一个默认的无参构造方法,如果写了,就会覆盖掉默认的无参构造方法;作用:在实例化对象时为公有属性进行赋值

public Myclass(int _age)
{
  //this代表当前 类 Myclass
  //this.Age 代表当前类中Age属性
  //_age 接收实例化对象时传入的值
    this.Age = _age;
}
//构造方法重载
public Myclass(int _age,string _name)
{  
    this.Age = _age;
    this.Name=_name;
}
//还可以使用this书写构造方法的重载(构造方法间的调用)
//相当于调用了public Myclass(int _age)
//这个构造方法为_age进行赋值,减少代码量
public Myclass(int _age, string _name):this(_age)
{
   // this.Age = _age;
    this.Name = _name;
}

4)普通方法:由访问修饰符(没有,默认为private),返回值类型,方法名构成

//如果此方法供类的外部使用一般为public
//如果此方法供类的内部使用一般为private
//void 表示没有返回值
//方法名一般也是首字母大写
public void SayHi(int age)
{
   Console.WriteLine("张三,今年{0}岁了",age);
}

实例化类的对象,调用类的公有属性与方法

static void Main(string[] args)
{
    //实例化对象并为属性赋初始值
    //赋初始值相当于调用类的构造方法
    //new的作用
    //1)实例化对象
    //2)在内存中开辟对象所需的空间
    //3)调用对象的构造方法
    Myclass ms1 = new Myclass(18);
    Myclass ms2 = new Myclass(18,"张三");
    //调用类的普通方法
    ms1.SayHi(20);
    Console.ReadKey();
}

面向对象的三大特性:封装,继承,多态

1 封装的体现:如被类封装的成员,通过访问修饰符约束外部访问此成员的级别;字段封装为属性,相关的方法封装在一起等,最终目的是减少内部的暴露对外提供统一的访问接口

访问修饰符:设置被修饰的类或其成员的访问级别(使用权限)

private 当前类中可以访问,类中成员的默认访问修饰符。

protected 当前类及子类中可以访问(在继承中用)

internal 当前程序集内部可以访问(类的默认访问修饰符)

protected internal 当前程序集或子类中(不同程序集也可以访问,相当于两者的并集)

public 在任何地方都可以被访问

只有报错信息为:“可访问性不一致”的错误,基本就是因为使用者与被使用者之间的级别不对等,一般是使用者的级别低于被使用者,解决方法提高使用者的级别至对等或高于被使用者

//如子类的可访问级别比父类的高
//默认 internal 
class Person
{   }
public class Student : Person
{  
  //父类Person 比 子类Student 的级别低
}
//其他情况之前说过不再赘述

2 继承是指类与类之间的关系;所有的类都继承Object类(相当于祖宗类)继承中基类与派生类相当于父类与子类

继承中有两大特性:

2.1)单根性:只有一个父类;使用接口可以实现多继承,两者区别:父类只能有一个(类比亲爹)只能写在子类的第一个位置;接口可以有多个(类比干爹)

2.2)传递性:子类的子类可以继承子类父类中的成员;相当于孙子可以继承爷爷的家产,但是孙子不是直接继承自爷爷而是通过父亲继承了爷爷的家产;因为在继承中只有子类继承父类的关系,没有子类继承爷类的关系,只是通过中间的父类达到了孙子继承爷爷的目的,从而实现类的传递性

使用继承的益处:1)代码重用 2)实现多态(使用了里氏替换原则LSP)便于后期的拓展与维护等

//父类
public class A
{
  public int Age{ get; set; }
}
//子类
public class B:A
{
  public string Name{ get; set; }
}
//A,B类自带无参数的构造方法
//实例化B的对象 B相当于有两个自动属性
//通过对象初始化器进行赋值
B b1=new B(){ Name = "张三", Age = 18 };

继承中的构造方法无法被继承,因此子类想要使用父类的构造方法时需要通过base关键字进行指定;可以使用this调用类内部自己其他的构造方法(构造方法的重载)

public class A
{
  //一个参数
    public A(int _age)
    {
        this.Age = _age;
    }
  //两个参数(只为演示this用法与下面B的构造方法无关)
  public A(int _age, char _gender)
       :this(_age)
  {    
    this.Gender=_gender;
  }
    public int Age { get; set; }
    public char Gender { get; set; }
}
public class B : A
{
  //此类 不需要Gender属性
    public B(int _age, string _name)
        : base(_age)//调用了父亲的这个构造方法
    {
      //B中省略下面Age的赋值
      //this.Age = _age;
        this.Name = _name;
      //它们的调用过程可通过调试查看
    }
    public string Name { get; set; }
}
//实例化B的对象
static void Main(string[] args)
{
   B b1 = new B(18, "张三");
  //在24代码这设置断点,通过逐语句进行调试,之前说过不再赘述
}

3 实现多态的三种方式:虚方法,抽象方法,接口

实现多态的目的:把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化

3.1 虚方法:如果父类的某些方法需要在子类中进行重写,可使用virtual进行标记,继承的子类可使用override进行重写继承下的方法,虚方法不是必须要重写的方法

class Program
{
    static void Main(string[] args)
    {
        Person p1 = new Person();
        p1.SayHi();
        Console.WriteLine("");
        Person p2 = new Student();
        //对虚方法进行了重写,因此调用子类重写后的方法
        //相当于子类调用了自己的方法
        p2.SayHi();
        Console.WriteLine("");
        //对虚方法没有进行重写,
        //相当于父类调用了自己的方法        
        Person p3 = new Teacher();
        p3.SayHi();
       Console.WriteLine("");
        //子类调用自己的方法
			 Teacher t1 = new Teacher();
			  t1.SayHi();
        Console.ReadKey();
    }
}
public class Person
{   
    //标记为虚方法
    public virtual void SayHi()
    {
        Console.WriteLine("此方法已被 virtual 标记为虚方法,子类可重新");
    }
}
public class Student : Person
{  
    public override void SayHi()
    {
        Console.WriteLine("重写了父类中的重名的虚方法");
    }
}
public class Teacher : Person
{
    //没有重写重名的方法最后加上new
    //在子类中用new修饰一个方法的目的
    //就是在子类中用该方法隐藏父类中对应的重名方法
    //这样对同一个方法而言用父类的实例对像来调用
    //和用子类的实例对象来调用就各不相同
    //实现了在子类中用子类方法隐藏父类的方法。
    public new void SayHi()
    {
        Console.WriteLine("隐藏了父类的同名方法");
    }
}

3.2 抽象类与抽象方法:使用abstract进行标记,抽象方法只能定义在抽象类中,抽象方法没有方法体,不能进行实例化,只能实例化为子类;属于光说不做,只能由后代的某个子类必须重写;

public abstract class T
{
      //抽象方法没有方法体
      public abstract void Eat();
      public abstract void Drink();
}
public abstract class T1 : T
{
    //继承了父类的Eat()与Drink()方法
    //只实现了Eat()方法
    public override void Eat()
    {
        Console.WriteLine("实现了 T 中的Eat()方法");
    }
    //自己又添加一个抽象方法  
    public abstract void Sleep();
    //因为Sleep()是抽象方法,因此 T1 必须是 抽象类
    //如果没有此抽象方法 T1 就是普通类  
  
    //由子类T2统一实现继承下来的Drink()与Sleep()抽象方法
}
public class T2 : T1
{
    public override void Drink()
    {
        Console.WriteLine("实现了 T1 的 Drink()方法 ");
    }
    public override void Sleep()
    {
        Console.WriteLine("实现了 T1 的 Sleep()方法 ");
    }
    //又重新实现了 T 中的方法
   //会覆盖掉 T1 中实现方法的内容
    public override void Eat()
    {
        Console.WriteLine("重新实现了 T 的 Eat()方法");
    }
}
//实例化子类
class Program
{
    static void Main(string[] args)
    {
        T test1 = new T2();
        test1.Eat();
        test1.Drink();
        Console.WriteLine("");
        T1 test2 = new T2();
        test2.Eat();
        test2.Drink();
        test2.Sleep();
        Console.WriteLine("");
        T2 test3 = new T2();
        test3.Eat();
        test3.Drink();
        test2.Sleep();
        Console.ReadKey();
    }
}

1)T1又添加了抽象方法,因此T1必须是抽象类

2)抽象方法的实现以最后实现的内容为准,相当于覆盖掉了之前实现的内容

虚方法与抽象方法的区别:

1)虚方法可以所在的类可以被实例化,也可以放在普通类,抽象类中

抽象类本身不能被实例化,只能实例化其子类,抽象方法只能定义在抽象类中

2)虚方法有方法体,在父类中必须进行实现,哪怕是空实现(方法体中没有代码)子类可以重写也可以不重写,没有强制性

抽象方法没有方法体,在父类中不能进行实现只能由子类进行实现(除非子类也是抽象类),继承的后辈子类只要没有进行实现,此抽象方法将一直具有强制性

3)不管是虚方法还是抽象方法都不能使用private进行修饰,因为这两种方法本身就是让其子类进行重写的,如果可以设置为private就失去了这些方法本身存在的意义

3.3 接口:使用interface进行定义,用于定义具有各种功能的方法,是一种能力,没有具体实现,与抽象方法一样都属于“光说不做”的类型

1)接口不能被实例化,只能实例化子类,接口中的方法也没有方法体

2)接口中只能有方法(方法,属性,索引器,事件)不能有字段,也没有修饰符(默认public)

3)可以实现多继承,同时解决类继承后体积庞大的问题,在实现接口的子类中必须全部实现接口中的方法,接口方法不能使用override,直接书写实现方法即可

4)实现方式:1)实现接口与显示实现接口其区别1)解决了方法重名的问题2)显示实现接口只能通过接口进行调用,子类调用不到

示例:麻雀与鹦鹉,继承一个父类同时实现了其他接口;提取父类(Bird)包含Show()方法;提取飞的能力(接口)与说话的能力(接口)

public class Bird
{
    //子类可以重写
    public virtual void Show()
    {
        Console.WriteLine("有翅膀会吃东西");
    }
}
public interface IFlyable
 {
     //隐式public
        void Fly();
 }
public interface ISayable
{
    void SayHi();
}
public class MQ : Bird, IFlyable
{
    public override void Show()
    {
      //调用父类的虚方法
        base.Show();
    }
  //实现接口,不能使用override
    public void Fly()
    {
        Console.WriteLine("麻雀在飞");
    }
}
//多继承时 父类在第一个,接口在后面
public class YW : Bird, ISayable, IFlyable
{
    public override void Show()
    {
      //重写了父类的方法
        Console.WriteLine("鹦鹉:有翅膀还能吃");
    }
    public void SayHi()
    {
        Console.WriteLine("鹦鹉学舌,会\"说话\"");
    }
    public void Fly()
    {
        Console.WriteLine("鹦鹉在飞");
    }
}
//实例化对象
static void Main(string[] args)
{
    IFlyable mq = new MQ();
    mq.Fly();
    Console.WriteLine("");
    IFlyable yw = new YW();
    yw.Fly();
    Console.WriteLine("");
    ISayable say = new YW();
    say.SayHi();
    Console.ReadKey();
}

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表