Fork me on GitHub

C#学习笔记(4)深入理解类上

4,1成员修饰符的顺序

在前面的内容中,你看到字段和方法的声明可以包括如public和private这样的修饰符。

类成员声明语句由下列部分组成:核心声明、一组可选的修饰符和一组可选的特性(attribute用于描述这个结构的语法如下。方括号表示方括号内的成分是可选的。

[特性]  [修饰符] 核心声明

可选成分如下。

修饰符:

(1)如果有修饰符,必须放在核心声明之前。

(2)如果有多个修饰符,可以任意顺序排列。

特性

(1)如果有特性,必须放在修饰符和核心声明之前。

(2)如果有多个特性,可以任意顺序排列。

至此,只解释了两个修饰符,即public和private。以后会介绍特性。例如,public static都是修饰符,可以一起修饰某个声明。因为它们都是修饰符,所以可以任何顺序放置。两行代码在语义上是等价的;

public static int MaxVal;

static public int MaxVal;

4.2静态字段

静态字段被类的所有实例共享,所有实例都访问同一内存位置。因此,如果该内存位置的值被一个实例改变了,这种改变对所有的实例都可见。
可以使用static修饰符将字段声明为静态,如:

class D
(
    int Mem1;    //实例字段
    static int Mem 2;    //静态字段

例如,上面代码声明了类D,它含有静态字段Mem2和实例字段Mem1。Main定义类D的两个实例。静态成员Mem 2是与所有实例分开保存的。并且从实例内部,访问或更新静态字段的语法和访问或更新其他成员字段一样。

因为Mem2是静态的,所以类D的两个实例共享一个Mem2字段。如果Meme2被改变了,个改变在两个实例中都能看到。成员Mem 1没有声明为static,所以每个实例都有自己的副本。

 

4.3 从类的外部访问静态成员

方法一:

就像实例成员,静态成员也可以使用点运算符从类的外面进行访问,但因为没有实例,所以访问静态成员的方法使用类名

形如

class D {

    static int Mem;

……

}

D.Mem = Value; //访问静态成员

 

代码:

using System;
 class A
 {
    public const int N = 10010;
    public static void display()
    {
        Console.WriteLine($"N = {N}");
    }
 }
 class Program
 {
    static void Main()
    {
        Console.WriteLine($"A.N = {A.N}");
        A.display();
    }
}

运行结果:

 

 

 

方法二:

使用using static声明,在该成员所属的类中包含一个using static声明。

样例如下

using static System.Console;

…………

WriteLine(“$hello world”);

 

静态成员的生存周期(静态成员的生命期与实例成员的不同):

只有在实例创建之后才产生实例成员,在实例销毁之后实例成员也就不存在了。但是即使类没有实例,也存在静态成员,并且可以访问。

 

4.4静态函数成员

(1)如同静态字段,静态函数成员独立于任何类实例。即使没有类的实例,仍然可以调态方法。

(2)静态函数成员不能访问实例成员,但能访问其他静态成员。

访问静态函数的方法与访问静态字段一致。

 

4.5其他静态类成员类型

方法√ 属性√ 构造函数√ 运算符√ 索引器× 事件√

字段√ 类型√ 常量×

 

4.6成员常量

特别说明:与C和C++不同,在C#中没有全局常量。每个常量都必须声明在类型内。

成员常量类似前一章所述的局部常量,只是它们被声明在类声明中而不是方法内,如下面的示例:

class MyClass

{

const int IntVal=100;//定义int类型常量

}

const double PI=3.1416;//错误:不能在类型声明之外声明

成员常量与前面讲的局部常量几乎一致不过是声明在类中。

 

4.7属性

4.7.1属性概述

属性是代表类实例或类中的数据项的成员。使用属性就像写入或读取一个字段,语法相同。

属性的特征:

(1)属性是命名的类成员。

(2)属性有类型。

(3)属性可以被赋值和读取。

(4)属性不一定为数据存储分配内存!
(5)属性会执行代码。

属性是一组(两个)匹配的、命名的、称为访问器的方法。

(1)set访问器为属性赋值。
(2)get访问器从属性获取值。

 

4.7.2属性声明和访问器

set和get访问器有预定义的语法和语义。可以把set访问器想象成一个方法,带有理参数,它“设置”属性的值。get访问器没有参数并从属性返回一个值。

set访问器:

(1)拥有一个单独的、隐式的值参,名称为value,与属性的类型相同;

(2)拥有一个返回类型void。

get访问器:

(1)没有参数;

(2)拥有一个与属性类型相同的返回类型;

形如:

 

Set与get说明:

set访问器中的隐式参数value是一个普通的值参。和其他值参一样,可以用它发送双数据到方法体或访问器块。在块的内部,可以像普通变量那样使用value,包括对它赋值。

get访问器的所有执行路径必须包含一条return语句,它返回一个属性类型的值。

访问器set和get可以以任何顺序声明,并且,除了这两个访问器外,属性上不允许有其他方法

 

4.7.3属性的示例

下面的代码展示了一个名为C1的类的声明示例,它含有一个名为MyValue的属性。

请注意,属性本身没有任何存储。取而代之,访问器决定如何处理发送进来的数据,以及

应将什么数据发送出去。

在代码之前在介绍一下如何使用属性:

要写入一个属性,在赋值语句的左边使用属性的名称。

要读取一个属性,把属性的名称用在表达式中。

例如,下面的代码包含一个名为MyValue的属性的声明。只需使用属性名就可以写入和读取属性,就好像它是一个字段名。

int MyValue//属性声明

{

set{ ...}get{…‥}

}

MyValue= 5;//赋值: 隐式调用set方法

z = MyValue;//表达式: 隐式调用get方法

属性会根据是写入还是读取来隐式地调用适当的访问器。不能显式地调用访问器,因为这样做会产生编译错误。

y = MyValue. get();//错误!不能显式调用get访问器

MyValue. set(5);//错误!不能显式调用set访问器

代码如下:

using System;
class C1
 {
    private int m_value = 10;
    pubilc int Myvalue //属性不分配内存
    {
        set { m_value = value; }
        get { return m_value}
    }
 }
 class Program
 {
    static void Main()
    {
        C1 c = new C1();
        Console.WriteLine("Myvalue: {0}", c.Myvalue);
        c.Myvalue = 20;
        Console.WriteLine("Myvalue: {0}", c.Myvalue);
    }
}

运行结果:

C#7.0为属性的getter/setter引入了另一种语法,这种语法使用表达函数体。这种语法只有在访问函数体由一个表达式组成的时候才能使用。(以后再详细介绍)

int MyValue

{

set=>value>100? 100: value;

get =>  theRealValue ;

}

 

4.7.4只读和只写属性

要想不定义属性的某个访问器,可以忽略该访问器的声明。

(1)只有get访问器的属性称为只读属性。只读属性能够安全地将一个数据项从类或类的实例中传出,而不必让调用者修改属性值。

(2)只有set访问器的属性称为只写属性。只写属性很少见,因为它们几乎没有实际用途。如果想在赋值时触发一个副作用,应该使用方法而不是属性。

(3)两个访问器中至少有一个必须定义,否则编译器会产生一条错误消息。

 

4.7.5属性是用来替代公有字段的方法之一

属性比公有字段更好,理由如下。

(1)属性是函数成员而不是数据成员,允许你处理输入和输出,而公有字段不行。

(2)属性可以只读或只写,而字段不行。

(3)编译后的变量和编译后的属性语义不同。

 

4.7.6自动属性

这里留一个坑以后再填

 

4.7.7静态属性

属性也可以声明为static。静态属性的访问器和所有静态成员一样,具有以下特点。

(1)不能访问类的实例成员,但能被实例成员访问。

(2)不管类是否有实例,它们都是存在的。

(3)在类的内部,可以仅使用名称来引用静态属性。

(4)在类的外部,正如前面描述的,可以通过类名或者使用using static结构来引用静态属性。

例如,下面的代码展示了一个类,它带有名为MyVa1ue的自动实现的静态属性。在Main的头三行,即使没有类的实例,也能访问属性。Main的最后一行调用一个实例方法,它从类的内部访问属性。

代码展示:

using System;
using static ConsoleTestApp.Trivial;
namespace ConsoleTestApp
{
    class Trivial
    {
        public static int MyValue { get; set; }
        public void PrintValue()
        { Console.WriteLine("Value from inside:{0}", MyValue); }

    }

    class Program
    {

        static void Main()
        {
            Console.WriteLine("Init Value:{0}", Trivial.MyValue);
            Trivial.MyValue = 10;
            Console.WriteLine("New Value:{O}", Trivial.MyValue);
            MyValue = 20;//从类的外部访问,但由于使用了using static,所以没有使用
            Console.WriteLine($"New Value:{MyValue}");
            Trivial tr = new Trivial();
            tr.PrintValue();
        }
    }
}

 

 

posted @ 2022-06-30 11:02  衔清风与共  阅读(207)  评论(0)    收藏  举报