2021-09-29

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

一、类的封装与继承

1.属性封装

C#提供了属性(property)这个更好的方法,把字段域和访问它们的方法相结合。对类的用户而言,属性值的读/写与字段域语法相同;对编译器来说,属性值的读/写是通过类中封装的特别方法get访问器和set访问器实现的。
属性的声明方法如下:
[属性集信息] [属性修饰符] 类型 成员名
{
访问器声明
}
其中:
 属性修饰符—与方法修饰符相同,包括 new、static、virtual、abstract、override 和 4种访问修饰符的合法组合,它们遵循相同的规则。
 类型—指定该声明所引入的属性的类型。
 成员名—指定该属性的名称。
 访问器声明——声明属性的访问器,可以是一个 get 访问器或一个 set 访问器,或者两个都有。
语法形式:
get // 读访问器
{
… // 访问器语句块
}
set // 写访问器
{
… // 访问器语句块
}
get 访问器的返回值类型与属性的类型相同,所以在语句块中的 return 语句必须有一个可隐式转换为属性类型的表达式。
set 访问器没有返回值,但它有一个隐式的值参数,其名称为 value,它的类型与属性的类型相同

using System;
namespace ConsoleApp3
{
    class TextBox
    {
        private string text;
        private string fontname;
        private int fontsize;
        private bool multiline;
        public TextBox()
        {
            text = "text1";
            fontname = "宋体";
            fontsize = 12;
            multiline = false;
        }
        public string Text
        {
            // Text属性,可读可写
            get
            { return text; }
            set
            { text = value; }
        }
        public string FontName
        {
            // FontName属性,只读属性
            get
            { return fontname; }
        }
        public int FontSize
        {
            // FontSize属性,可读可写
            get
            { return fontsize; }
            set
            { fontsize = value; }
        }
        public bool MultiLine
        {
            // MultiLine属性,只写
            set
            { multiline = value; }
        }
    }
    class Test
    {
        static void Main(string[] args)
        {
            TextBox Text1 = new TextBox();
            // 调用Text属性的get访问器
            Console.WriteLine("Text1.Text= {0} ", Text1.Text);
            // 调用Text属性的set访问器
            Text1.Text = "这是文本框";
            Console.WriteLine("Text1.Text= {0} ", Text1.Text);
            // 调用FontName属性的get访问器
            Console.WriteLine("Text1.Fontname= {0} ", Text1.FontName);
            // 调用FontSize属性的set访问器
            Text1.FontSize = 36;
            // 调用FontSize属性的get访问器
            Console.WriteLine("Text1.FontSize= {0} ", Text1.FontSize);
            // 调用MultiLine属性的set访问器
            Text1.MultiLine = true;
            Console.Read();
        }
    }
}



属性是字段的自然扩展,当然属性也可作为特殊的方法使用,并不要求它和字段域一一对应,所以属性还可以用于各种控制和计算。
using System;
namespace ConsoleApp3
{
    class Point
    {
        int x, y;
        public int X
        {
            get
            { return x; }
        }
        public int Y
        {
            get
            { return y; }
        }
        public Point()
        { x = y = 0; }
        public Point(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    }
    class Label
    {
        Point p1 = new Point();
        Point p2 = new Point(5, 10);
        public int Width			// 计算两点之间在水平坐标轴上的投影长度
        {
            get
            {
                return p2.X - p1.X;
            }
        }
        public int Height			// 计算两点之间在垂直坐标轴上的投影长度
        {
            get
            {
                return p2.Y - p1.Y;
            }
        }
    }
    class Test
    {
        static void Main(string[] args)
        {
            Label Label1 = new Label();
            Console.WriteLine("Label1.Width= {0} ", Label1.Width);
            Console.WriteLine("Label1.Height= {0} ", Label1.Height);
            Console.Read();
        }
    }
}

2.类的继承

代码如下(示例):

using System;
namespace ConsoleApp3
{ // 定义基类Shape
    public class Shape
    {
        protected string Color;
        public Shape() {}
        public Shape(string Color)
        {
            this.Color = Color;
        }
        public string GetColor()
        {
            return Color;
        }
    }
    // 定义Circle类,从Shape类中派生
    public class Circle : Shape
    {
        private double Radius;
        public Circle(string Color, double Radius)
        {
            this.Color = Color;
            this.Radius = Radius;
        }
        public double GetArea()
        {
            return System.Math.PI * Radius * Radius;
        }
    }
    // 派生类Rectangular,从Shape类中派生
    public class Rectangular : Shape
    {
        protected double Length, Width;
        public Rectangular()
        {
            Length = Width = 0;
        }
        public Rectangular(string Color, double Length, double Width)
        {
            this.Color = Color;
            this.Length = Length;
            this.Width = Width;
        }
        public double AreaIs()
        {
            return Length * Width;
        }
        public double PerimeterIs()         // 周长
        {
            return (2 * (Length + Width));
        }
    }
    // 派生类Square,从 Rectangular类中派生
    public class Square : Rectangular
    {
        public Square(string Color, double Side)
        {
            this.Color = Color;
            this.Length = this.Width = Side;
        }
    }
    class TestInheritance
    {
        static void Main(string[] args)
        {
            Circle Cir = new Circle("orange", 3.0);
            Console.WriteLine("Circle color is {0},Circle area is {1}", Cir.GetColor(), Cir.GetArea());
            Rectangular Rect = new Rectangular("red", 13.0, 2.0);
            Console.WriteLine("Rectangualr color is {0},Rectangualr area is {1},Rectangular perimeter is {2}",Rect.GetColor(), Rect.AreaIs(), Rect.PerimeterIs());
            Square Squ = new Square("green", 5.0);
            Console.WriteLine("Square color is {0},Square Area is {1}, Square perimeter is {2}", Squ.GetColor(),Squ.AreaIs(), Squ.PerimeterIs());
        }
    }
}




using System;
namespace ConsoleApp3
{ // 定义基类Shape
    public class Person
    {
        protected string Phone = "444-555-666";
        protected string Name = "李明";
        public void GetInfoPerson()
        {
            Console.WriteLine("Phone: {0}", Phone);
            Console.WriteLine("Name: {0}", Name);
        }
    }
    class Employee : Person
    {
        public string ID = "ABC567EFG";
        public void GetInfoEmployee()
        {
            // 调用基类Person的GetInfo方法 
            base.GetInfoPerson();
            Console.WriteLine("Employee ID: {0}", ID);
        }
    }
    class TestClass
    {
        static void Main(string[] args)
        {
            Employee Employees = new Employee();
            Employees.GetInfoEmployee();
        }
    }
}

3.派生类的构造函数

当创建派生类的对象时,会展开一个链式的构造函数调用,在这个过程中,派生类构造函数在执行它自己的函数体之前,首先显式或隐式地调用基类构造函数。类似地,如果这个基类也是从另一个类派生而来的,那么这个基类的构造函数在执行之前也会先调用它的基类构造函数,以此类推,直到Object类的构造函数被调用为止。

using System;
namespace ConsoleApp3
{
    public class Grandsire
    {
        public Grandsire()
        {
            Console.WriteLine("调用Grandsire的构造函数");
        }
    }
    public class Father : Grandsire
    {
        public Father()
        {
            Console.WriteLine("调用Father的构造函数");
        }
    }
    public class Son : Father
    {
        public Son()
        {
            Console.WriteLine("调用Son的构造函数");
        }
    }
    class BaseLifeSample
    {
        static void Main(string[] args)
        {
            Son s1 = new  Son();
            Console.Read();
        }
    }
}



下面程序描述了派生类构造函数的格式,以及在初始化对象时构造函数的调用次序。
using System;
namespace ConsoleApp3
{
    class Point
    {
        private int x, y;
        public Point()
        {
            x = 0; y = 0;
            Console.WriteLine("Point() constructor : {0} ", this);
        }
        public Point(int x, int y)
        {
            this.x = x;
            this.y = y;
            Console.WriteLine("Point(x,y) constructor : {0} ", this);
        }
    }
    class Circle : Point
    {
        private double radius;
        public Circle()     		// 默认约定调用基类的无参构造函数Point()
        {
            Console.WriteLine("Circle () constructor : {0} ", this);
        }
        public Circle(double radius) : base()
        {
            this.radius = radius;
            Console.WriteLine("Circle (radius) constructor : {0} ", this);
        }
        public Circle(int x, int y, double radius) : base(x, y)
        {
            this.radius = radius;
            Console.WriteLine("Circle (x, y, radius) constructor : {0} ", this);
        }
    }
    class Test
    {
        static void Main(string[] args)
        {
            Point a = new Point();
            Circle b = new Circle(3.5);
            Circle c = new Circle(1, 1, 4.8);
            Console.Read();
        }
    }
}

二、多态的实现

1.方法重载

一个方法的名字、形参个数、修饰符及类型共同构成了这个方法的签名,应用中经常需要为同名的方法提供不同的实现,如果一个类中有两个或两个以上的方法同名,但它们的形参个数或类型有所不同,这是允许的,这属于不同的方法签名。若仅仅是返回类型不同的同名方法,编译器是不能识别的。下面通过一个例子来介绍方法的重载。

using System;
namespace ConsoleApp3
{
    class Myclass              // 该类中有4个不同版本的max方法
    {
        public int max(int x, int y)
        {
            return x >= y ? x : y;
        }
        public double max(double x, double y)
        {
            return x >= y ? x : y;
        }
        public int max(int x, int y, int z)
        {
            return max(max(x, y), z);
        }
        public double max(double x, double y, double z)
        {
            return max(max(x, y), z);
        }
    }
    class Test
    {
        static void Main(string[] args)
        {
            Myclass m = new Myclass();
            int a, b, c;
            double e, f, g;
            a = 10; b = 20; c = 30;
            e = 1.5; f = 3.5; g = 5.5;
            // 调用方法时,编译器会根据实参的类型和个数调用不同的方法
            Console.WriteLine("max({0},{1})= {2} ", a, b, m.max(a, b));
            Console.WriteLine("max({0},{1},{2})= {3} ", a, b, c, m.max(a, b, c));
            Console.WriteLine("max({0},{1})= {2} ", e, f, m.max(e, f));
            Console.WriteLine("max({0},{1},{2})= {3} ", e, f, g, m.max(e, f, g));
            Console.Read();
        }
    }
}

2.运算符重载

运算符重载包括一元运算符重载、二元运算符重载以及用户定义的数据类型转换,下面简单介绍前两种情况。
如果有一个复数Complex类对一元运算符“++”重载,可以写成:
public static Complex operator ++(Complex a)
{
//TODO…
}
对二元运算符“+”可以写成:
public static Complex operator +(Complex a, Complex b)
{
//TODO…
}
一元运算符有一个参数,二元运算符有两个参数。重载运算符必须以 public static 修饰符开始。
可以重载的运算符包括:
 一元运算符——+  ! ~ ++   true false;
 二元运算符——+  * / % & | ^ << >> == != > < >= <=。

using System;
namespace ConsoleApp3
{
    class Complex
    {
        double r, v;  					//r+ v i
        public Complex(double r, double v)
        {
            this.r = r;
            this.v = v;
        }
        // 二元运算符“+”重载
        public static Complex operator +(Complex a, Complex b)
        {
            return new Complex(a.r + b.r, a.v + b.v);
        }
        // 一元运算符“-”重载
        public static Complex operator -(Complex a)
        {
            return new Complex(-a.r, -a.v);
        }
        // 一元运算符“++”重载
        public static Complex operator ++(Complex a)
        {
            double r = a.r + 1;
            double v = a.v + 1;
            return new Complex(r, v);
        }
        public void Print()
        {
            Console.Write(r + " + " + v + "i\n");
        }
    }
    class Test
    {
        static void Main(string[] args)
        {
            Complex a = new Complex(3, 4);
            Complex b = new Complex(5, 6);
            Complex c = -a;
            c.Print();
            Complex d = a + b;
            d.Print();
            a.Print();
            Complex e = a++;          // 先赋值后++
            a.Print();
            e.Print();
            Complex f = ++a;          // 先++后赋值
            a.Print();
            f.Print();
        }
    }
}

三、虚拟方与方法覆盖

1.继承中的方法隐藏及其局限

【例】 试用隐藏基类成员方法的方式,在运行时执行指定派生类方法的功能。
本例定义了一个基类 Shape,含有字段域 width 和 height,分别表示形状的宽和高,并定义了一个 area 方法,用来求形状的面积。它的派生类Triangle(三角形)和Trapezia(梯形)都用关键字new修饰了 area 方法,表明这是有意隐藏该方法的。若不加new修饰,为了警示,编译器会发出警告信息。

using System;
namespace ConsoleApp3
{
    class Shape
    {
        protected double width;
        protected double height;
        public Shape()
        { width = height = 0; }
        public Shape(double x)
        { width = height = x; }
        public Shape(double w, double h)
        {
            width = w;
            height = h;
        }
        public double area()
        { return width * height; }
    }
    class Triangle : Shape      		// 三角形
    {
        public Triangle(double x, double y) : base(x, y) { }
        new public double area()    	// 加new隐藏基类的area方法
        {
            return width * height / 2;
        }
    }
    class Trapezia : Shape   		// 梯形
    {
        double width2;
        public Trapezia(double w1, double w2, double h) : base(w1, h)
        {
            width2 = w2;
        }
        new public double area()		// 加new隐藏基类的area方法
        {
            return (width + width2) * height / 2;
        }
    }
    class Test
    {
        static void Main(string[] args)
        {
            Shape A = new Shape(2, 4);
            Triangle B = new Triangle(1, 2);
            Trapezia C = new Trapezia(2, 3, 4);
            Console.WriteLine("A.area= {0} ", A.area());   // 调Shape的area方法
            Console.WriteLine("B.area= {0} ", B.area());   // 调Triangle的area方法
            Console.WriteLine("C.area= {0} ", C.area());   // 调Trapezia的area方法
            A = B;
            Console.WriteLine("A.area= {0} ", A.area());   // 试图调Triangle的area方法失败
            A = C;
            Console.WriteLine("A.area= {0} ", A.area());   // 试图调Trapezia的area方法失败
            Console.Read();
        }
    }
}

上段代码本来是想分别调用 Triangle 的 area 方法计算三角形面积,调用 Trapezia 的 area 方法计算梯形面积,但程序执行的仍然是基类的 area 方法,只计算了抽象的“形状面积”:
A.area=width * height=1×2=2≠width * height / 2
A.area=width * height=2×4=8≠(width + width2) * height / 2

2.虚方法覆盖技术

(1)虚方法的重载
在类的方法前加上关键字 virtual,就声明了一个虚方法。通过对虚方法的重载,实现在程序运行过程中确定调用的方法。
【例】 虚方法的重载。
在 A 类定义中提供了非虚的方法 F 和虚方法 G,派生类 B 对方法 F 进行隐藏,而对虚方法 G 则使用 override 关键字实现了覆盖。这样一来,语句“A a = b”中的a就仍是一个b对象.

using System;
namespace ConsoleApp3
{
    class A
    {
        public void F() { Console.WriteLine("A.F"); }
        public virtual void G() { Console.WriteLine("A.G"); }
    }
    class B : A
    {
        new public void F() { Console.WriteLine("B.F"); }
        public override void G() { Console.WriteLine("B.G"); }
    }
    class Test
    {
        static void Main(string[] args)
        {
            B b = new B();
            A a = b;
            a.F();
            b.F();
            a.G();
            b.G();
            Console.Read();
        }
    }
}

(2)派生类方法覆盖基类方法
下面通过虚方法重载的机制,利用覆盖技术实现调用派生类方法计算三角形和梯形面积。
【例】Shape类中的方法area用virtual修饰,而在派生类Triangle和Trapezia中用关键字override修饰area方法,这样就可以在程序运行时决定调用哪个类的area方法。

using System;
namespace ConsoleApp3
{
    class Shape
    {
        protected double width;
        protected double height;
        public Shape()
        { width = height = 0; }
        public Shape(double x)
        { width = height = x; }
        public Shape(double w, double h)
        {
            width = w;
            height = h;
        }
        public virtual double area()   //virtual
        { return width * height; }
    }
    class Triangle : Shape      		// 三角形
    {
        public Triangle(double x, double y) : base(x, y) { }
         public override double area()    	//override
        {
            return width * height / 2;
        }
    }
    class Trapezia : Shape   		// 梯形
    {
        double width2;
        public Trapezia(double w1, double w2, double h) : base(w1, h)
        {
            width2 = w2;
        }
         public override double area()		//override
        {
            return (width + width2) * height / 2;
        }
    }
    class Test
    {
        static void Main(string[] args)
        {
            Shape A = new Shape(2, 4);
            Triangle B = new Triangle(1, 2);
            Trapezia C = new Trapezia(2, 3, 4);
            Console.WriteLine("A.area= {0} ", A.area());   // 调Shape的area方法
            Console.WriteLine("B.area= {0} ", B.area());   // 调Triangle的area方法
            Console.WriteLine("C.area= {0} ", C.area());   // 调Trapezia的area方法
            A = B;
            Console.WriteLine("A.area= {0} ", A.area());   // 试图调Triangle的area方法成功
            A = C;
            Console.WriteLine("A.area= {0} ", A.area());   // 试图调Trapezia的area方法成功
            Console.Read();
        }
    }
}

四、抽象类与抽象方法

抽象类是一种特殊的基类,并不与具体的事物相联系。抽象类的定义使用关键字abstract。将“图形”定义为抽象类,并由它派生出“圆形”、“四边形”这样一些可以产生具体实例的普通类。需要注意的是,抽象类不能被实例化,它只能作为其他类的基类。
将Shape类定义为抽象类:
public abstract class Shape
{
//TODO…
}
在抽象类中也可以使用关键字 abstract 定义抽象方法,要求所有的派生非抽象类都要重载实现该方法。引入抽象方法的原因在于,抽象类本身是一种抽象的概念,有的方法并不要求具体的实现,而是让派生类去重载实现。Shape 类中 GetArea 方法本身没什么具体的意义,而只有到了派生类Circle类和Rectangular类才可以计算具体的面积。
抽象方法的语法:
public abstract double GetArea();
派生类重载实现为:
public override double GetArea()
{
//TODO…
}
【例】 抽象类和抽象方法的实现。

using System;
namespace ConsoleApp3
{
    // 定义抽象基类Shape
    public abstract class Shape
    {
        protected string Color;
        public Shape() { }
        public Shape(string Color)
        {
            this.Color = Color;
        }
        public string GetColor()
        {
            return Color;
        }
        public abstract double GetArea();           // 定义抽象方法
    }
    // 定义Circle类,从Shape类中派生
    public class Circle : Shape
    {
        private double Radius;
        public Circle(string Color, double Radius)
        {
            this.Color = Color;
            this.Radius = Radius;
        }
        // 实现抽象方法
        public override double GetArea()
        {
            return System.Math.PI * Radius * Radius;
        }
    }
    // 定义Rectangular类,从Shape类中派生
    public class Rectangular : Shape
    {
        protected double Length, Width;
        public Rectangular(string Color, double Length, double Width)
        {
            this.Color = Color;
            this.Length = Length;
            this.Width = Width;
        }
        // 实现抽象方法
        public override double GetArea()
        {
            return Length * Width;
        }
        public double PerimeterIs()         // 周长
        {
            return (2 * (Length + Width));
        }
    }
    // 派生类Square,从Rectangular类中派生
    public class Square : Rectangular
    {
        public Square(string Color, double Side) : base(Color, Side, Side) { }
    }
    class TestAbstract
    {
        static void Main(string[] args)
        {
            Circle Cir = new Circle("orange", 3.0);
            Console.WriteLine("Circle color is {0},Circle area is {1}", Cir.GetColor(), Cir.GetArea());
            Rectangular Rect = new Rectangular("red", 13.0, 2.0);
            Console.WriteLine("Rectangualr color is {0},Rectangualr area is {1},Rectangular perimeter is { 2 }",Rect.GetColor(), Rect.GetArea(), Rect.PerimeterIs()); Square Squ = new Square("green", 5.0);
            Console.WriteLine("Square color is {0},Square Area is {1}, Square perimeter is { 2 }",Squ.GetColor(), Squ.GetArea(), Squ.PerimeterIs());
            Console.Read();
        }
    }
}

抽象类只能作为基类,由其他类继承,不能被实例化。相对应地还有一种不能被其他类继承的类,叫密封类,使用sealed关键字定义。如将Rectangular类定义为密封类:
public sealed class Rectangular:Shape
{
// TODO…
}
这样Rectangular类中的派生类Square将不再保留,否则,程序编译时会报错。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值