9. 结构和类

1 结构

结构就是几个数据组成的数据结构,它与类共享几乎所有相同的语法,但结构比类受到的限制更多。

1.1 结构概述

结构是一种值类型,通常用来封装一组相关的变量,结构中可以包括构造函数、常量、字段、方法、属性、运算符、时间和嵌套类型等。但如果要同时包括上述几种成员,则应该考虑使用类。

结构实际是将多个相关变量包装成为一个整体使用。在结构体中的变量,可以是相同、部分相同,或完全不同的数据类型。例如,将公司的职员看做一个结构体,可以将个人信息放入结构体中,主要包含姓名、年龄、出生年月、性别、机关、婚否、职务、

结构具有一下特点:

  1. 结构是值类型
  2. 向方法传递结构时,结构是通过传值方式传递的而不是作为引用传递的。
  3. 结构的实例化可以不适用new运算符
  4. 结构可以声明构造函数,但他必须带参数
  5. 一个结构不能从另一个结构或类继承。所有结构都继承自System.ValueType,后者继承自System.Object。
  6. 结构可以实现接口
  7. 在结构中初始化实例字段是错误的
  8. 在结构声明中,除非字段被声明为const或static,否则无法初始化

C#中使用关键字struct关键字来声明结构:

结构修饰符 struct 结构名
{
}

1.2 结构的使用

通过一个实例说明如何在程序中使用结构:
实例:

class Program
{
    static void Main(string[] args)
    {
        Rect rect1;
        rect1.width = 3;
        rect1.height = 4;
        Rect rect2 = new Rect(4, 6);
        Console.WriteLine(rect1.Area());//12
        Console.WriteLine(rect2.Area());//24
        Console.ReadLine();
    }
}
public struct Rect
{
    public double width;
    public double height;
    public Rect(double x, double y)
    {
        width = x;
        height = y;
    }
    public double Area()
    {
        return width * height;
    }

}

2 面向对象概述

面向对象思想是人类最自然的一种思考方式,它将所有预处理的问题抽象为对象,同时了解这些对象具有哪些相应的属性以及展示这些对象的行为,以解决面向对面临的一些实际问题,这样就在程序开发中引入了面向对象设计的概念。面向对象设计实质上就是对现实世界的对象进行建模操作。

2.1 对象

在面向对象中,算法与数据结构被看做一个整体,称为对象。现实世界中任何类的对象都具有一定的属性和操作,也总能用数据结构与算法俩者合而为以来描述,所以可以用个下面的等式来定义对象和程序。
对象=(算法+数据结构)
程序 = (对象 + 对象 + …)
现实世界中,随处可见的一种事物就是对象,对象时事物存在的实体。通常会将对象划分为俩个部分,即动态部分和静态部分。
静态部分:被称为属性,任何对象都会具备其自身的属性。如一个人的身高、胖瘦、年龄、性别等。
动态部分:被称为行为,如一个人可以吃饭、跑步、微笑说话等。

2.2 类

类就是同一事物的统称,类是构造对象所以来的规范。
类是时间事物的抽象称呼,而对象则是这个事物相对应的实体。
类是封装对象的属性和行为的载体,反过来说具有相同属性和行为的一类实体被称为类。

在C#语言中,类对象的行为是以方法的形式定义的,对象的属性是以成员变量的形式定义的,而类包括对象的属性和方法。

2.3 封装

面向对象程序设计具有封装性、继承性、多态性。

封装是面向对象编程的核心思想,将对象的属性和行为封装起来,而将对象的属性和行为封装起来的载体就是类,类通常对客户隐藏其实现细节,这就是封装的思想。
采用封装的思想保证了类内部数据结构的完整性,应用该类的用户不能轻易直接操作数据结构,而只能执行类允许公开的数据。这样就避免了外部对内部数据的影响,提高了程序的可维护性。

2.4 继承

类与类之间同样具有关系,继承思想可以缩短软件开发的时间,复用已经定义好的类可以提高系统性能,减少系统在使用过程中出现错误的几率。

继承主要利用特定属性之间的共有属性,子类可以共享父类的属性和方法,但父类不可以共享子类的属性和方法。

2.5 多态

键父类对象应用于子类的特征就是多态。
在提到多态的同时,不得不提到抽象类和接口,因为多态的实现并不依赖具体类,而是以来抽象类和接口。
在多态的机制中,比抽象类更为方便的是将抽象类定义为接口。由抽象方法组成的集合就是接口。

3 类

类是一种数据结构,他可以包含数据成员(常量和域)函数成员(方法、属性、时间、索引器、运算符、构造函数和析构函数)和嵌套类型。
类支持继承,继承是一种使子类(派生类)可以对基类进行扩展和专用化的机制。
类实际上是某种类型的对象定义变量和方法的原型,它表示现实生活一类共同特征的事物的抽象,是面向对象编程的基础。

3.1 类的概念

类是对象概念在面向对象编程语言中的反映,是相同对象的集合。类描述了一系列在概念上有相同含义的对象,并为这些对象同意定义了编程语言上的属性和方法。
类是C#中功能最为强大的数据类型,向结构一样,类也定义了数据的类型和行为。与结构不同,类支持继承,而继承是面向对象编程的基础部分。

3.2 类的声明

声明一个汽车类

public class Car
{
	public int number;
	public string color;
	public string brand;
}

常用类修饰符

修饰符名称限制范围
new仅允许在嵌套类声明时使用,表明类中隐藏了由基类中继承而来的、与基类中同名的成员。
public不限制对该类的访问
protected只能从其所在的类和所在的子类进行访问
internal只有其所在的类才能访问
private只有.NET中的应用程序或库才能访问
abstract抽象类,不允许建立类的实例
sealed密封类,不允许被继承

3.3 构造函数和析构函数

构造函数和析构函数是类中比较特殊的俩种成员函数,主要用来对对象进行初始化和回收对象资源。一般来说,对象的生命周期以构造函数开始,以析构函数结束。如果一个类含有构造函数,在实例化该类的对象的时候就会调用,如果含有析构函数,则会在销毁对象时调用。
构造函数的名字和类名相同,析构函数和构造函数的名字相同,但构希函数要在名字前加一个波浪号。当退出含有该对象的成员时,析构函数将自动释放这个对象所占用的内存空间。

3.3.1 构造函数的概念及使用

构造函数是在创建给定类型的对象时执行的类方法。构造函数具有和类相同的名称,它通常初始化新对象的数据成员。

实例:

class Program
{
    static void Main(string[] args)
    {
        Program p = new Program();
        Console.WriteLine(p.z); //8
        Console.ReadLine();
    }
    public int x = 3;
    public int y = 5;
    public int z = 0;
    public Program()
    {
        z = x + y;
    }
}

注意!:
不带参数的构造函数称为默认构造函数。无论何时,只要使用new运算符实例化对象,并且不为new提供任何参数,就会调用默认构造函数。

3.3.2 析构函数的概念及使用

析构函数是以类名加~来命名的。.NET Framework类库有垃圾回收的功能,当某个类的实例被认为是不再有效,并符合析构函数条件时,.NET Framework类库的垃圾回收功能就会调用该类的析构函数实现垃圾回收。

实例:

class Program
{
    ~Program()
    {
        Console.WriteLine("析构函数自动调用"); //8
    }
    static void Main(string[] args)
    {
        Program p1 = new Program();
    }
   
    
}

注意:
一个类中只能有一个析构函数,并且无法调用析构函数,它是被自动调用的。

3.4 对象的创建及使用

对象可以认为是在一类事物中抽象除某一个特例,通过这个特例来处理这类事物出现的问题。在C#中语言通过new操作符来创建对象。每实例化一个对象就会自动调用一次构造函数,实质上这个过程就是创建对象的过程,在C#在语言中使用new操作符调用构造函数创建对象。

3.4.1 对象的创建

Test test = new Test();
Test test = new Test("a");
--Test:类名
--test:Rest类对象
--new:创建对象操作符
--"a":构造函数参数

每个对象否都是相互独立的,在内存中占据独立的内需地址,并且每个对象都具有自己的生命周期,当一个对象的声明周期结束时,对象变成了垃圾,由.Net自带的垃圾回收机制处理。
在C#语言中对象和实例事实上可以通用。

3.4.2 访问对象的属性和行为

当用户使用new操作符创建一个对象后,可以使用 对象.类成员 来获取对象的属性和行为。对象的属性和行为在类中是通过成员变量和成员方法的形式来表示的,所以当对象获取类成员时,也就相应的获取了对象的属性和行为。

实例:

class Program
{
    
    static void Main(string[] args)
    {
        Program p1 = new Program();
        Program p2 = new Program();
        p2.i = 60;
        Console.WriteLine("第一个实例对象调用变量I的结果" + p1.i);
        p1.call();
        Console.WriteLine("第二个实例对象调用变量I的结果" + p2.i);
        p2.call();
        Console.ReadLine();
    }
    int i = 47;
    public void call()
    {
        Console.WriteLine("调用call方法");
        for (int i = 0; i <3; i++)
        {
            Console.Write(i + " ");
            if (i == 2)
                Console.Write("\n");
        }
    }
    public Program() { }
}

3.4.3 对象的引用

在C#语言中尽管一切都可以看做对象,但真正的操作标识符实质上是一个引用。
语法如下:

Book  book = new Book();	//类名 对象引用名称

注意:
引用只是存放一个对象的内存地址,并非存放一个对象,严格的说引用和对象是不同的,但是可以将这种区别忽略,如可以简单的说book是Book类的一个对象,而事实上应该是book包含Book对象的一个引用。

3.4.4 对象的销毁

每个对象都有声明周期吗,当对象的生命周期结束时,分配给该对象的内存地址将会被回收。在其他语言中需要手动回收废弃的对象,但是C#拥有一套完整的垃圾回收机制,用户不必担心废弃的对象占用内存,垃圾回收器将回收无用的但占用内存的资源。

在谈到垃圾回收机制之前,首先需要了解何种对象会被.NET垃圾回收器视为垃圾。主要包括以下俩种情况:

  1. 对象引用超过其作用范围(有效范围),则这个对象将被视为垃圾
  2. 将对象赋值为null

3.5 this关键字

在类中定义setName方法,并将方法的参数赋值给类中的成员变量。

private boid setName(string name )
{
	this.name = name;
}

在上述代码中可以看到,成员变量语setName()方法中的形式参数名称相同,都为name,那么该如何在类中区分使用的是哪一个变量呢?在C#语言中规定使用this关键字来代表类对象中的引用,this关键字被隐式地用于引用对象的成员变量和方法。

this引用的就是本类的一个对象,在局部变量或方法参数覆盖了成员变量时,就要添加this关键字明确引用的是类成员还是局部变量或方法参数。

this除了可以调用成员变量或成员方法之外,还可以作为方法的返回值


public Book getBook()
{
	return this; 	// 返回Book类引用
}

3.6 类与对象的关系

类是一种抽象的数据类型,但是其抽象的程度可能不同,而对象就是一个类的实例。
类是具有相同或相似结构、操作和约束规则的对象组成的集合,而对象是某一类的具体实例,每一个类都是具有某些共同特征的对象的抽象。

4 类的面向对象特性

4.1 类的封装

C#中可以使用类来达到数据封装的效果,这样就可以使数据与方法封装成单一元素,以便于通过方法存取数据。除此之外,还可以控制数据的存取方式。
在面向对象编程中,大多数都是以类作为封装的基本单位。类将数据和操作数据的方法结合成一个单位。设计类时,不希望直接存取类中的数据,而是希望通过方法来存取数据,这样就可以达到封装数据的目的,方便以后的维护升级,也可以在操作数据时多一层判断。
封装还可以解决数据存取的权限问题,可以使用封装将数据隐藏起来,形成一个封闭的空间,然后可以设置哪些数据只能在这个空间中使用,哪些数据可以在空间外部使用。一个类中包含敏感数据,有些人可以访问,有些人不能访问,如果不对这些数据的访问加以限制,后果将会非常严重。在编写程序时,要对类的成员使用不同的访问修饰符,从而定义它们的访问级别。
封装的目的是增强安全性和简化编程,使用者不必了解具体的使用细节,只要通过外部接口这一特定的访问权限来使用类的成员即可。

实例:

class Program
{
    static void Main(string[] args)
    {
        MyClass mc = new MyClass();
        mc.X = 3;
        mc.Y = 5;
        Console.WriteLine(mc.Add());
        Console.ReadLine();
    }
}
class MyClass
{
    private int x = 0;
    private int y = 0;
    public int X { get; set; }
    public int Y { get; set; }
    public int Add() 
    {
        return X + Y;
    }
}

4.2 类的继承

继承是面向对象编程最重要的特性之一。任何类都可以从另一个类继承,这个类拥有它继承的类的所有成员。在面向对象编程中,被继承的类称为父类或基类。C#中提供了类的继承机制,但只支持单继承,而不支持多继承,即在C#中一次只允许继承一个类,不能同时继承多个类。

利用类的继承机制,用户可以通过增加、修改或替换类中的方法对这个类进行扩充,以适应不同的应用要求。利用继承,程序开发人员可以在已有类的基础上构造新类,这一性质使类支持分类的概念。在日常生活中很多东西都很有条理,那是因为它们有着很好的层次分类。如果不用层次分类,则需要对每个对象都定义其所有的性质。使用继承后,每个对象就可以只定义自己的特殊性质,每一层的对象只需要定义本身的性质,其他性质可以从上一层继承下来。

继承的基本思想是基于某个父类的扩展,指定出一个新的子类,子类可以继承父类原有的属性和方法,也可以增加原来父类所不具备的属性和方法,或者直接重写父类中的某些方法。

使用public声明类成员,可以让该类成员被基类和子类同时访问,同时也可以别外部的代码访文。
使用protected声明的类成员,只有子类才能访问,基类和外部代码都不能访问。

实例:

class Program
{
    static void Main(string[] args)
    {
        MyClass mc = new MyClass();
        MyClass2 mc2 = new MyClass2();
        mc.X = 3;
        mc.Y = 5;
        mc2.X = 3;
        mc2.Y = 5;
        mc2.Z = 2;
        Console.WriteLine(mc.Add()); //8   8   10
        Console.WriteLine(mc2.Add());
        Console.WriteLine(mc2.Add2());
        Console.ReadLine();
    }
}
class MyClass
{
    private int x = 0;
    private int y = 0;
    public int X { get; set; }
    public int Y { get; set; }
    public int Add() 
    {
        return X + Y;
    }
}
class MyClass2:MyClass
{
    private int z = 0;
    public int Z { get; set; }
    public int Add2()
    {
        return X + Y + Z;
    }
}

继承并不只是扩展父类的功能,还可以重写父类的成员方法。重写就是在子类中将父类的成员方法名称保留,重写成员方法的实现内容,更改成员方法的存储权限,或是修改成员方法的返回值类型。

在继承中还有一种特殊的重写方式,子类与父类的成员方法返回值、方法名称、参数类型及个数完全相同,唯一不同的是方法实现内容,这种特殊的重写方式被称为重构。
在重写父类方法时,修饰方法的修饰权限只能从小的范围到大的范围改变,即重写时不能降低方法的修饰权限范围。

4.3 类的多态

多态使子类的实例可以直接赋予基类的变量(不需要强制转换),然后直接就可以通过这个变量调用子类的方法。利用多态可以使程序具有良好的扩展性,并且可以对所有类对象进行通用的处理。

实例:

using System;
namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Program p = new Program();
            p.draw(new Square());
            p.draw(new Parallelogramgle());
            Console.ReadLine();
        }
        private Program[] qtest = new Program[6];
        private int nextIndex = 0;
        public void draw(Program p)
        {
            if (nextIndex<qtest.Length)
            {
                qtest[nextIndex] = p;
                Console.WriteLine(nextIndex);
                nextIndex++;
            }
        }
    }
    class Square:Program
    {
        public Square()
        {
            Console.WriteLine("正方形");
        }
    }
    class Parallelogramgle : Program
    {
        public Parallelogramgle()
        {
            Console.WriteLine("平行四方形");
        }
    }


}

从本实例的运行结果可以看出,以不同的类对象为参数调用draw()方法可以处理不同的图形问题、使用多态节省了开发和维护时间,因为程序员无需在所有的子类中定义执行相同功能的方法,避免了大量重复代码的开发,同时只要实例化一个继承父类的子类对象即可调用相应的方法,这里只要维护父类中的方法即可。

注意:
在派生于同一个类的不同对象上执行任务时,多态是一种极为有效的技巧,使用的代码最少。可以把一组对象放到一个数组中然后调用它们的方法,在这种情况下多态的作用就体现出来了,这些对象不必是相同类型的对象。当然如果它都继承自某个类,可以把这些类放到一个数组中。如果这些对象都有同名方法,就可以调用每个对象的同名方法。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值