07 创建并管理类和对象

1.理解分类

英语里面的类(class)是分类(classification)的词根。设计类的过程就是对信息进行分类,将相关信息放到有意义的实体中。所有人都会分类——并非只有程序员才会。例如,所有汽车都有通用的行为(都能转向、制动、加速等)和通用的属性(都有方向盘、发动机等)和通用的属性(都有方向盘、发动机等)。

 

2.封装的目的

封装是定义类时的重要原则。它的中心思想是:使用类的程序不应关心类内部如何工作。程序只需创建类的实例并调用类的方法。只要方法能做到它们宣称能做到的事情,程序就不关心它们具体如何实现。例如在调用Console.Write方法时,肯定不会想去了解Console类将数据输出到屏幕的复杂细节。封装有时称为信息隐藏,它实际有两个目的:

(1)将方法和数据合并到类中,也就是为了支持分类

(2)控制对方法和数据的访问,也就是为了控制类的使用

 

3.定义并使用类

C#用class关键字定义新类。类的数据和方法放在类的主体中(两个大括号之间)。以下Circle类包含方法(计算圆的面积)和数据(圆的半径):

class Circle

{

    int radius;


    double Area()

    {

        return Math.PI * radius * radius;//PI,即圆周率

    }

}

类主体包含的是一般的方法(如Area)和字段(如radius)。记住,C#术语将类中的变量称为字段。

Circle类的使用方式和之前用到的其他类型相似。以Circle为类型名称创建变量,再以有效的数据初始化它。下面是一个例子:

Circle c;      //创建Circle变量

c = new Circle;   //初始化

注意,这里使用了new关键字。以前在初始化int或float变量时是直接赋值:

int i;

i = 42;

但类类型的变量不能像以前那样赋值。一个原因是C#没有提供将字面值赋给类变量的语法,例如不能像下面这样写:

Circle c;

c = 42;

等于42的Circle是什么意思?另一个原因涉及“运行时”对类类型的变量的内存进行分配与管理的方式,这方面的详情将在下一篇博客讨论。目前只需要接受这样一个事实:new关键字将新建类的实例。所谓“类的实例”,更通俗的说法就是“对象”。

但是,可以直接将类的实例赋值给相同类型的另一个变量,例如:

Circle c;

c = new Circle();

Circle d;

d = c;

但如果这样赋值,实际发生的事情或许并不是你想象的那样。下一篇博客将解释具体原因。

 

4.控制可访问性

类的默认访问级别是(internal )

internal : 同一程序集中的任何代码都可以访问该类型或成员,但其他程序集不可以访问。 

public : 同一程序集的其他任何代码或引用该程序集的其他程序集都可以访问该类型或成员。

可以用public和private关键字修改字段或方法的定义,决定它们是否能从外部访问。

(1)只能从类内部访问的方法或字段是私有的。声明私有方法或字段需要在声明前添加private关键字。该关键字是默认的,但作为良好编程实战,应显示将字段和方法声明为private,以避免困惑。

(2)方法或字段如果既能从类的内部访问,也能从外部访问,就说它是公共的。声明公共方法或字段需要在声明前添加pulic关键字

下例是修改过的Circle类。这次Area方法声明为公共方法,radius声明为私有字段:

class Circle

{

    private int radius;

    public double Area()

    {

        return Math.PI * radius * radius;//PI,即圆周率

    }

}

我们会发现现在还无法初始化radius字段。现在先讲一下构造器来初始化字段,后面的博客中会介绍get(),set()方法对字段的操作。

 

4.1使用构造器

使用new关键字创建对象时,“运行时”必须根据类的定义构造对象。必须从操作系统申请内存区域,在其中填充类定义的字段,然后调用构造器执行任何必要的初始化。

构造器是在创建类的实例时自动运行的方法。它与类同名,能获取参数,但不能返回任何值(即使是void)。每个类至少要有一个构造器。如果不提供构造器,编译器会自动生成一个什么都不做的默认构造器。自己写默认构造器很容易——添加与类同名的公共方法,不返回任何值就可以了。下例展示了有默认构造器的Circle类,这个自己写的构造器能将radius字段初始化为0:

class Circle

{

    private int radius;

    public Circle()//默认构造器

    {

        radius = 0;

    }

    public double Area()

    {

        return Math.PI * radius * radius;//PI,即圆周率

    }

}

4.2重载构造器

之前博客讲方法的时候我们已经讲了方法的重载,现在我们来讲讲构造器的重载,其实构造器也是方法。

如下:

class Circle

{

    private int radius;
    public Circle()//默认构造器

    {
        radius = 0;
    }

    public Circle(int initialRadius)//重载构造器

    {
        radius = initialRadius;
    }

    public double Area()

    {
        return Math.PI * radius * radius;//PI,即圆周率
    }

}

然后可在新建Circle对象时调用该构造器,如下所示:

Circle c;

c = new Circle(45);

注意:一旦为类写了任何构造器,编译器就不再自动生成默认构造器。所以,一旦写了构造器,让它接收一个或多个参数,同时还想要默认构造器,就必须自己写一个无参构造器

 

4.3分部类(partial关键字)

一个功能齐全的类可能相当大,C#允许将类的源代码拆分到单独的文件中。这样,大型类的定义就可用较小的、更易管理的部分进行组织。

比如通用Windows平台(UWP)应用程序采用的就是这种代码组织技术。开发者可编辑的源代码在一个文件中维护,窗体布局变化时由VS生成的代码在另一个文件中维护。

类被拆分到多个文件中之后,要在每个文件中使用partial(分部)关键字定义类的不同部分。例如,假定Circle类被拆分到两个文件中,分别是circ1.cs(包含构造器)和circ2.cs(包含方法和字段)

Circ1.cs

partial class Circle

{

    public Circle()//默认构造器
    {
        radius = 0;
    }

    public Circle(int initialRadius)//重载构造器
    {
        radius = initialRadius;
    }
}

Circ2.cs

partial class Circle

{

    private int radius;
    public double Area()
    {
        return Math.PI * radius * radius;//PI,即圆周率
    }

}

注:编译拆分到多个文件的类,必须向编译器提供全部文件

 

5.理解静态方法和数据

之前在Circle类中用过Math类的PI字段。有没有觉得使用PI字段(Math.PI)的方式有点儿奇怪?是直接在类的上面调用方法,也是直接在类的上面使用字段,而不是先创建Math类的对象,再在这个对象的基础上调用方法和使用字段。到底发生了什么,为什么能这样写?

C#所有方法都必须在类的内部声明。但如果把方法或字段声明为static,就可使用类名调用方法或访问字段。下面展示了Math类的Sqrt方法具体如何声明:

class Math

{
    public static double Sqrt(double d)
    {

        ……

    }

    ……
}

可以像下面这样调用Sqrt方法:

double d = Math.Sqrt(42,24);

静态方法不依赖类的实例,不能访问类的任何实例字段或实例方法。相反,只能访问标记为static的其他方法和字段。

 

5.1创建共享字段

静态字段能在类的所有对象之间共享(非静态字段则局部于类的实例)。在下例中,每次新建Circle对象,Circle构造器都使Circle类的静态字段NumCircles递增1:

class Circle

{

    private int radius;
    public static int NumCircles = 0;

    public Circle()//默认构造器
    {
        radius = 0;
        NumCircles++;
    }


    public Circle(int initialRadius)//重载构造器
    {
        radius = initialRadius;
        NumCircles++;
    }

    public double Area()
    {
        return Math.PI * radius * radius;//PI,即圆周率
    }

}

NumCircles字段由所有Circle对象共享,所以每次新建实例,NumCircles++;语句递增的都是相同的数据。从类外访问NumCircles字段,要以Circle作为前缀,而不是以类的实例名称作为前缀。例如:

Console.WriteLine($"Number of Circle objects:{Circle.NumCircles}");

注:静态方法也称为“类方法”,但静态字段通常不叫“类字段”,就叫“静态字段”或者“静态变量”

 

5.2使用const关键字创建静态字段

用const关键字声明的字段称为常量字段,是一种特殊的静态字段,它的值永远不会改变。const字段虽然也是静态字段,但声明时不用static关键字。只有数值类型(例如int或doube)、字符串类型和枚举类型的字段才能声明为const字段。

例如,在真正的Math类中,PI就被声明为const字段:

class Math

{
    …

    public const double PI = 3.141592653589793 ;

}

5.3理解静态类

C#允许声明静态类。静态类只能包含静态成员(使用该类创建的所有对象都共享这些成员的单一拷贝)。静态类纯粹作为工具方法和字段的容器使用。静态类不能包含任何实例数据或方法。另外,使用new操作符创建静态类的对象没有意义,编译器会报错。为了执行初始化,静态类允许包含一个默认构造器,前提是该构造器也被声明为静态。其他任何类型的构造器都是非法的,编译器会报错。

如果定义自己的Math类,其中只包含静态成员,应该像下面这样写:

public static class Math

{

    public  static double Sin(double x){…}

    public  static double Cos(double x){…}

    public  static double Sqrt(double x){…}

}

5.4静态using语句

任何时候调用静态方法或引用静态字段,都必须指定方法或字段所属的类,比如Math.Sqrt或Console,WritrLine。静态using语句允许将类引入作用域,以便在访问静态成员时省略类名。这类似于用普通的using语句将命名空间引入作用域。

例如:

using static System.Math;

using static System.Console;

…

var root = Sqrt(99.9);

WriteLine($"The square root of 99.9 is {root}");

5.5匿名类

匿名类是没有名字的类。虽然听起来奇怪,但这种类有时相当好用。后面讲查询表达式的时候,会讲到它们的作用。

创建匿名类的办法是以new关键字开头,后跟一对{},在大括号中定义想在类中包含的字段和值,如下所示:

myAnonymousObject = new { Name = "John", Age = 47};

该类包含两个公共字段,名为Name(初始化为字符串“John”)和Age(初始化为整数47),编译器根据用于初始化字段的数据类型推断字段类型。

我们会发现,定义匿名类时,编译器为该类生成只有它自己知道的名称,这带来了一个有趣的问题:

既然不知道类名,如何创建正确类型的对象,并把类的实例分配给它?在上例中,myAnonymousObject变量的类型是什么?

答案是根本不知道类型是什么——这正是匿名类的意义。我们可以使用var声明隐式类型的变量,问题就解决了,如下所示:

var myAnonymousObject = new { Name = "John", Age = 47};

之前讲过,使用var关键字,对变量进行初始化的表达式是什么类型,编译器就用这个类型创建变量。

输出匿名内部类的字段值:

Console.WriteLine($"Name:{myAnonymousObject.Name} Age:{myAnonymousObject.Age}");

 

注意:匿名类虽然有时很好用,但内容存在着很多限制。例如:匿名类只能包含公共字段,字段必须全部初始化,不可以是静态,而且不能定义任何方法。(之后的博客会将匿名类做更多的拓展)

 

参考书籍:《Visual C#从入门到精通》

发布了46 篇原创文章 · 获赞 53 · 访问量 3715

猜你喜欢

转载自blog.csdn.net/qq_38992372/article/details/104982811
07