C#学习之C#面向对象编程(类与实例、虚拟类、抽象类、封装类、静态类、范型)

介绍

​ C#语言集成了Java和C++的优点,在全面的基础上发展了面向对象的概念,比如委托和范型,是从C++语言的函数指针和模版概念进化而来的,而单根的面向对象结构,即所有的对象都是继承于object类,这在很大程度上来自Java语言的思想。

主要内容:C#创建类和实例、虚拟类、抽象类、封装类、静态类、范型类、类的属性、方法、事件、C#语言委托远离和用法、匿名类、拓展方法

类和实例
1. 类和实例

​ C#引入类的概念,以全面支持对象编程。使用class关键字定义一个类,可以在类前面添加各种修饰符。

​ 类把实例抽象成一个概念,而实例具有类定义的这些特征。

​ 可以通过"[]“符号对类进行设置,一个比较重要的设置是”[Serializable]",这个设置表示当前的类是可序列化的。序列化的主要作用是类实例数据在传递的时候维持原样

[Serializable]
public class Circle
{

}

​ C#语言使用new关键字调用类的构造方法创建类的实例。

Circle MyCircle = new Circle();
2. 继承类

​ 所谓继承,就是一个类引入另一个类的内容,包括属性、方法是事件等。引入其他类的,称为子类,提供引入的类,称为父类。在子类中能够不必深;就调用父类的属性和方法。

​ 继承是面向对象机制中很重要的特征,经常作为标准用于判断编程语言是否位真正的面向对象语言。

​ C#是单继承的面向对象语言,而我们熟悉的Java也是单继承的语言,当C++是多继承的语言。

using System;

namespace ExtendsDemo
{
    public class Employee
    {
        public string Name
        {
            get;
            set;
        }
        public int Age
        {
            get;
            set;
        }
    }
    public class Manager : Employee
    {
        public Manager(string ManagerName, int ManagerAge) : base()
        {
            Name = ManagerName;
            Age = ManagerAge;
        }
    }
  	/*
  		子类构造函数中的“:base()”,表示先调用父类的构造方法
  	*/
}
3. 接口

​ 接口也是面向对象编程中的重要概念之一。许多精通面向对象的开发人员都建议针对接口编程,二不输出针对类编程。

​ 接口在更多的意义上是声明一种规范,继承接口的类都必须遵循这个规范。

​ C#编程语言的接口,具有以下特征:

  • 接口不能被实例化,也不能包含字段
  • 接口的索引、属性、方法、事件等内容,只可声明而不可具体实现
  • 继承A接口的B接口,具有A接口的特征
  • 接口可以被多重继承,即接口或类可以同时继承多个接口
using System;
namespace InterfaceDemo
{
    public interface IEntry
    {
        string EmployeeRecords
        {
            get;
            set;
        }
        void Train(int EmployeeID);
    }
}
using System;
namespace InterfaceDemo
{
    public class NewEmployee
    {
        public int NewEmployeeID
        {
            get;
            set;
        }
        /// <summary>
        /// 保存新员工数据
        /// </summary>
        /// <param name="employeeToSave">接口</param>
        public void SaveNewEmployee(IEntry employeeToSave)
        {
            // 获取接口中定义的属性
            string RecordsData = employeeToSave.EmployeeRecords;
            // 调用接口中定义的方法
            employeeToSave.Train(NewEmployeeID);
        }
    }
}

4. 实现接口

​ 在C#语言中,继承接口的类必须实现接口定义的内容,否则就会出现编译错误。

using System;

namespace InterfaceDemo
{
    /// <summary>
    /// 新员工类,继承就职IEntry接口
    /// </summary>
    class Employee : IEntry
    {
        public string TrainRecords
        {
            get;
            set;
        }
        // 实现员工档案的属性
        string IEntry.EmployeeRecords
        {
            get;
            set;
        }
        // 实现员工档案的方法
        void IEntry.Train(int EmployeeID)
        {
            TrainRecords = "公司制度培训和薪酬制度培训";
        }
      	/*
      		显式方式实现,在属性和方法名之前添加IEntry前缀,表名当前属性或者方法实现了IEntry接口
      		不能在属性或者方法前添加public前缀,因为接口声明的属性和方法必须允许外部程序访问,否则这种声明就没有意义
      	*/
    }
    /// <summary>
    /// 老员工类,继承就职IEntry接口
    /// </summary>
    public class Record : IEntry
    {
        public string TrainRecords
        {
            get;
            set;
        }
        // 实现员工档案的属性
        public string EmployeeRecords
        {
            get;
            set;
        }
        /// <summary>
        /// 实现员工档案培训的方法
        /// </summary>
        /// <param name="EmployeeID"></param>
        public void Train(int EmployeeID)
        {
            TrainRecords = "薪酬制度培训";
        }
      	/*
      		隐式方法实现,不在属性或方法前添加"IEntry"前缀,必须将属性或方法的访问控制符定义为"public",非e编译程序不认为这个类实现了接口
      	*/
    }
}

注意:接口的显式实现如果不声明访问权限修饰符,那么默认为public,如果添加访问权限修饰符,那么必须是public

5. 继承接口

​ 继承接口有两个含义,类继承接口,以及接口继承接口,类继承接口,必须要实现接口的声明。

​ 在一个系统中,客户的需求可能随时在改变,原有接口的修改意味着继承这个接口的所有类,都必须修改以实现接口,为了避免这种牵一发而动全身的灾难出现,我们可以借助于接口的继承来适应规范的改变。

using System;

namespace InterfaceDemo
{
    public interface IEntryExamine : IEntry
    {
        void Examine(int EmployeeID);
    }
    /// <summary>
    /// 新员工类,继承就职IEntry接口
    /// </summary>
    class Employee : IEntryExamine
    {
        public string TrainRecords
        {
            get;
            set;
        }
        // 实现员工档案的属性
        string IEntry.EmployeeRecords
        {
            get;
            set;
        }
        // 实现员工档案的方法
        void IEntry.Train(int EmployeeID)
        {
            TrainRecords = "公司制度培训和薪酬制度培训";
        }
        void IEntryExamine.Examine(int EmployeeID)
        {
            TrainRecords += "需要进行考试";
        }
    }
    /// <summary>
    /// 老员工类,继承就职IEntry接口
    /// </summary>
    public class Record : IEntry
    {
        public string TrainRecords
        {
            get;
            set;
        }
        // 实现员工档案的属性
        public string EmployeeRecords
        {
            get;
            set;
        }
        /// <summary>
        /// 实现员工档案培训的方法
        /// </summary>
        /// <param name="EmployeeID"></param>
        public void Train(int EmployeeID)
        {
            TrainRecords = "薪酬制度培训";
        }
    }
}

​ 可以使用接口的多继承,来完善员工入职的模型。

​ 接口的继承和多重继承,不仅为C#编程语言中类的继承增加了灵活性,更重要的是能以可扩展的方式,规范了类的设计和编码实现。

虚拟类、抽象类和封装类
1. 虚拟类

​ 虚拟类并不完全是虚拟的,致所有被称为“虚拟”,是因为C#编程语言借鉴了C++的“虚拟函数”的概念。在C++编程语言中,只有纯虚函数是没有实现代码的,所以“虚拟”的说法并不能确切表示虚拟类的特点,更多上是为了使用C++开发人员的习惯。

​ 实际上,完全可以在C#编程语言的虚拟方法中编写实现方法的代码。

​ 在C#编程语言中,虚拟类是一种可以在子类中覆盖其方法或者属性的类。

​ C#程序在类的内部,使用virtual声明一个方法或者属性,飙车在子类中可以覆盖这个方法或者属性。在子类的属性或方法声明之前,使用override关键字,表示当前方法或属性将覆盖父类定义的方法或属性。

​ 覆盖具有以下的原则:

  • 不能覆盖非虚方法、非虚属性或静态方法、静态属性
  • 可以覆盖的方法或属性必须是virtual、abstract或override的。因为子类继承了父类的可覆盖特征,所以override的方法或属性还可以在子类中覆盖
  • 覆盖的方法或属性,不能更改原方法或属性的访问控制符、参数列表、返回数据类型和名称
  • 不能使用修饰符new、static、virtual或abstract修改覆盖的方法或属性

重写、重载、覆盖的概念

项目重写重载覆盖
关键字newoverride
位置同一个类内部子类子类
访问控制可以不一致可以不一致必须一致
名称必须一致必须一致必须一致
参数列表必须不一致可以不一致必须一致
返回类型可以不一致可以不一致必须一致
using System;
namespace DifferentClassDemo
{
    public class MyWebPage
    {
        /// <summary>
        /// 显示类名称的方法,声明为virtual,表示可以在子类中覆盖该方法
        /// </summary>
        /// <param name="Language"></param>
        /// <returns></returns>
        protected virtual string ShowName(string Language)
        {
            switch (Language)
            {
                case "中文":
                    return "我的页面";
                case "English":
                    return "MyWebPage";
                default:
                    return "";
            }
        }
        /// <summary>
        /// 方法重载,参数类型不一致
        /// </summary>
        /// <returns></returns>
        protected string ShowName()
        {
            return "我的页面";
        }
    }
    /// <summary>
    /// 错误界面,继承自MyWebPage
    /// </summary>
    public class ErrorPage : MyWebPage
    {
        /// <summary>
        /// 方法重写,访问控制不一致
        /// </summary>
        /// <returns></returns>
        public new string ShowName()
        {
            return "错误页面";
        }
    }
    /// <summary>
    /// 登陆界面,继承自MyWebPage
    /// </summary>
    public class LoginPage : MyWebPage
    {
        /// <summary>
        /// 覆盖,与父类的方法一致
        /// </summary>
        /// <param name="Language"></param>
        /// <returns></returns>
        protected override string ShowName(string Language)
        {
            switch (Language)
            {
                case "中文":
                    return "登陆页面";
                case "English":
                    return "LoginPage";
                default:
                    return "";
            }
        }
    }
}

​ 事实证明,虚拟类的虚拟方法具有很灵活的实现方式,这里的“虚拟”是从C++语言参考而来的概念,并不意味着虚拟方法不能在类中实现,只是在父类实现的方法,很有机会被子类覆盖得面目全非,失去了原来的功能而已。

2. 抽象类

​ 抽象的本意是抽取某些事物具有代表性的特征,作为一个概念来描述这些事物的共性。

​ 抽象的含义在这里表现为不能创建实例,抽象类只声明了需要实现的内容,具体的功能代码在继承它的子类中编写。

​ C#语言中的抽象类,仅仅提供了子类所必须包含的内容,而不能够创建实例。抽象类的作用和接口有些类似,当并不完全一样。在C#编程语言中,使用abstract关键字声明一个抽象类,抽象类有以下的特点:

  • 抽象类不能使用new关键字创建类实例
  • 抽象类允许簇拥非抽象成员
  • 抽象类不能同时又是封装的
  • 继承抽象类的非抽象类,必须实现所有的抽象成员
using System;
namespace DifferentClassDemo
{
    /// <summary>
    /// 表示形状的类
    /// </summary>
    public abstract class Shape
    {
        public int BasePointX
        {
            get;
            set;
        }
        public int BasePointY
        {
            get;
            set;
        }
        public abstract int MaxLength
        {
            get;
            set;
        }
        public abstract int MaxWidth
        {
            get;
            set;
        }
        public abstract decimal GetArea();
    }
    public class Rectangle : Shape
    {
        public override int MaxLength
        {
            get;
            set;
        }
        public override int MaxWidth
        {
            get;
            set;
        }
        public override decimal GetArea()
        {
            return MaxLength * MaxWidth;
        }
    }
}
3. 封装类

​ 抽象类不能声明为封装类,对于抽象类而言,抽象类可以被继承,实际上必须被继承,是不能创建实例的,封装类刚好相反,可以创建实例而不能被继承。

​ C#中不允许将一个类同时设置为抽象和封装,因为这样做是没有任何意义的。

​ C#编程语言使用关键字“sealed”表示一个封装类,封装类不能被继承。同样,“sealed”可以用于属性和方法,表示不能够在子类中覆盖。

using System;
namespace DifferentClassDemo
{
    /// <summary>
    /// 只能创建一个实例的类
    /// </summary>
    public sealed class Singleton
    {
        static Singleton instance = null;
        private Singleton()
        {

        }
        public static Singleton Instance
        {
            get
            {
                if(instance == null)
                {
                    instance = new Singleton();
                }
                return instance;
            }
        }
    }
}

​ 这个案例是典型的Singleton模式的实现方式之一,为了禁止外部程序创建重复的类实例,Singleton类需要屏蔽外部程序访问构造函数,同时只允许通过只读的Instance静态属性获取唯一的Singleton类实例。

​ 如果不把类声明成封装类,仍然可以通过继承的途径,重复创建实例。所以,必须堵上所有继承来创建Singleton类实例的途径,而使Singleton类不能被继承,就应该使用sealed关键字声明Singleton,也就是说,Singleton是一个封装类。

​ 因为封装在运行的时候不考虑继承、重载等因素,所以执行效率不比封装的类更高,但是封装类不能被继承,所以封装性能不足,在程序中编写封装类的时候,应当充分考虑系统设计的要求。

静态类和范型类

​ 静态类和范型类更多意义上是为了设计与编程上的方便而引入的,所谓静态类,就是不能创建实例的类,所有的属性和方法都通过类名称来调用。相对于静态类而言,范型类对于提高代码重用性的作用更为明显,范型的愿望其实很简单,就是在声明的时候不确定数据类型,在调用时才确定数据类型,范型的引入显著提高了代码的重用性。

​ 静态类和范型类注意作用是提高程序效率,并且使代码具有较高的重用性。虽然静态类看起来很死板,当Virtual Studio的LINQ和拓展方法等新技术都是借助静态方法来实现的,所以静态方法间接地提高了代码的重用性,仅仅把静态类当作全局变量来调用,还不能充分发挥它的作用。

1. 静态类

​ 在C#编程语言中,有一种用static关键字来声明的静态类,使用静态类或者Singleton模式可以满足全局变量和全局方法的要求。静态类并不能创建实例,所有的调用都是使用类名称进行,这相当于无论何时,静态类总能提供一个所有类都能调用的变量或者方法。

​ 在一个类中对静态的静态属性赋值,然后在另一个类中调用这个静态属性,因为没有创建类实例的过程,所有静态属性都能吧值是一个类原原本本地传递到另一个类中。静态类和设计模式中的Singleton模式有异曲同工之妙。

​ 静态类不是为了实现全局变量而设计的,静态类的真正作用,并不仅仅为了实现全局变量,C#语言的静态类有以下的特点:

  • 静态类的所有成员都是静态的,无论是属性还是方法,都使用static关键字声明
  • 静态类的构造方法也是静态的,静态构造方法不能在子类中继承,并且不能带有private、public等控制访问符,也不能带有参数
  • 不能直接调用静态类的构造函数,在调用静态类成员时会自动执行而且仅执行一次
using System;
namespace DifferentClassDemo
{
    /// <summary>
    /// 将数字字符转换成十六进制字符串的静态类
    /// </summary>
    public static class StrringToHexCode
    {
        /// <summary>
        /// 静态方法,将字符串转换成十六进制字符串
        /// </summary>
        /// <param name="value">需要转换的字符串</param>
        /// <returns>已经转换了的字符串</returns>
        public static string ConvertToHexCode(string value)
        {
            return Int32.Parse(value).ToString("x");
        }
        /// <summary>
        /// 拓展静态方法,将字符串转换成十六进制字符串
        /// </summary>
        /// <param name="value">需要转换的字符串</param>
        /// <returns>已经转换了的字符串</returns>
        public static string ToHexCode(this string value)
        {
            return Int32.Parse(value).ToString("x");
        }
      	/*
      		拓展方法的使用:
      		string HexCode = "5000".ToHexCode();
      	*/
    }
}

​ 可以在静态类中写拓展方法,使用静态类的静态方法,使各种数据类型具有极强的拓展性。

​ 静态类这种特殊的类,大量用于基础架构的实现。

2. 范型类

​ 范型编程历来就是比较高级的编程理念,实现起来也是非常困难。

​ C++编程语言使用了模块的概念,使C++编程语言能够支持范型编程,C#编程语言的范型和C++编程语言的模版在实现的机制上稍有不同,但在使用上几乎没有区别。

​ .NET Framework从2.0就开始支持范型数据类型,这是范型对传统的面向对象编程有相当的冲击力。首先,一切对象基于object类的结构,被认为是“弱类型”的,相当于“强类型”的类,弱类型主要的劣势在于编译时不尽兴严格的数据类型检查,点a的执行速度也相对较慢。

​ 因为强类型编程提出了类型参数话的要求,简单的说,就是声明一个方法的参数时,一般情况下都是以“数据类型 参数名”的形式来声明的,因为强类型编程语言要严格检查参数的数据类型,所以这种代码在强类型的编程语言中重用性不高。

​ 这时,提供了另一种方法,生;参数的时候,暂时不指定参数的数据类型,用一个“T”符号先把位置占了,以后再把特定的数据类型替换上去。这个“T”符号已经被业界用了很久。

​ 范型编程带来的好处有以下三个方面:

  • 降低强制转换时装箱操作带来的成本和风险
  • 为C#编程语言的强类型编程提供了支持
  • 提高C#程序代码的重用性
using System;
using System.Collections;
namespace DifferentClassDemo
{
  	/*
  		在范型类声明时使用where语句限定范型的数据类型,称为范型约束,代码中的“where T:class”语句,限定EmployeeList的元素只能是引用类型的数据类型
  	*/
    public class EmployeeList<T> : CollectioinBase
        where T : class
    {
        public T this[int index]
        {
            get
            {
                return (T)List[index];
            }
            set
            {
                List[index] = value;
            }
        }
        public int Add(T value)
        {
            return List.Add(value);
        }
        public void Remove(T value)
        {
            List.Remove(value);
        }
    }
}
using System;
using System.Collections;
namespace DifferentClassDemo
{
    public class Employee
    {
        public string Name
        {
            get;
            set;
        }
        public int Age
        {
            get;
            set;
        }
    }
    public class Manager
    {
        public string Position
        {
            get;
            set;
        }
        public string Department
        {
            get;
            set;
        }
    }
    public class Test
    {
        static void Main(string[] args)
        {
            EmployeeList<Employee> CurrentEmployees = new EmployeeList<Employee>();
            CurrentEmployees.Add(new Employee() { Name="Jack", Age = 54});
            EmployeeList<Manager> CurrentManagers = new EmployeeList<Manager>();
            CurrentManagers.Add(new Manager() { Department="技术部", Position="程序员"});
        }
    }
}
  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
面向对象编程(Object-oriented programming,简称 OOP)是一种程序设计范型,主要关注数据的表示和处理方法。它将数据和操作数据的方法组合成一个整体,即“对象”。在面向对象编程中,对象是程序的基本单元,它可以具有数据和方法。类是对象的模板,它描述了对象的属性和方法。 1. 类和对象:类是一种数据类型,它定义了一组属性和方法,是对象的模板。对象是类的实例,它拥有类定义的属性和方法。在面向对象编程中,对象是程序的基本单元,所有的操作都是通过对象来实现的。 2. 继承:继承是面向对象编程的一个重要特性,它允许一个类继承另一个类的属性和方法。被继承的类称为父类或基类,继承它的类称为子类或派生类。继承可以减少代码的重复,提高了代码的复用性。 3. 多态:多态是面向对象编程的另一个重要特性,它允许不同的对象对相同的消息做出不同的响应。多态可以提高程序的灵活性和可扩展性。 4. 接口:接口是一种规范,它定义了一个类或对象应该具有的方法和属性,但并不实现这些方法和属性。接口可以提高程序的可维护性和可扩展性。 5. 抽象类:抽象类是一种不能被实例化的类,它定义了一些抽象方法,这些抽象方法必须在子类中被实现。抽象类可以提高程序的灵活性和可扩展性。 面向对象编程的优点包括代码的重用性、可维护性、可扩展性和代码的可读性。同时,面向对象编程也有一些缺点,如性能问题、复杂性和难以学习等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wells Wang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值