.net第七章------类成员


前言

  • 类成员的基本组成

  • 字段和常量

  • 方法

  • 属性

  • 索引器

  • 运算符重载

  • 构造函数和析构函数

  • 嵌套类

类成员的基本组成

  • 两种主要的类成员类型
  • 描述状态的数据成员和描述操作的函数成员
  • 类成员是静态成员或实例成员
    在这里插入图片描述

静态成员和实例成员

  • 静态成员属于类,被这个类的所有实例所共享
  • 实例成员属于对象(类的实例),每一个对象都有实例成员的不同副本

静态成员(static)

  • 必须通过类名来引用

  • 一个静态字段共享同一个存储位置,只有一个副本

  • 静态函数成员属于类的成员,在其代码体内不能直接引用实例成员

实例成员

  • 必须通过对象实例来引用

  • 实例字段属于类的实例,每个实例分别包含各实例字段的单独副本

  • 实例函数成员作用于类的给定实例,在其代码体内既可以使用实例成员,也可以直接引用类的静态成员

例子

class Counter
    {
        public int number;      //实例字段
        public static int count;   //静态字段
        public Counter()       //构造函数
        {
            count = count + 1; number = count;
        }
        public void showInstance()
        {
            Console.Write("object{0} :", number);  //正确:实例方法内可直接引用实例字段
            Console.WriteLine("count={0}", count);//正确:实例方法内可直接引用静态字段
        }
        public static void showStatic()
        {
            //Console.Write("object{0} :", number); //错误:静态方法内不能直接引用实例字段
            Console.WriteLine("count={0}", count); //正确:静态方法内可以直接引用静态字段
        }
    }
class CounterTest
    {
        public static void Main()
        {
            Counter c1 = new Counter();           //创建对象
            c1.showInstance();                   //正确:用对象调用实例方法
            //c1.showStatic();                     //错误:不能用对象调用静态方法
            Console.Write("object{0} :", c1.number); //正确:用对象引用实例字段
            //Console.Write("object{0} :", Counter.number); //错误:不能用类名引用实例字段
            //Console.WriteLine("count={1}", c1.count); //错误:不能用对象名引用静态字段
            Counter.showStatic();                   //正确:用类名调用静态方法
            //Counter.showInstance ();               //错误:不能用类名调用实例方法
            Counter c2 = new Counter();             //创建对象
            c1.showInstance(); c2.showInstance(); Console.ReadKey();
        }
    }

this关键字

  • 引用类的当前实例

  • this关键字只能在实例构造函数、实例方法、实例访问器中使用

  • 静态成员方法中不能使用this关键字

  • 如果定义了局部变量与实例字段重名,则直接采用实例字段名称将引用局部变量,而不是对象的实例字段

  • 引用实例字段,应采用:

  • this.实例字段

访问修饰符

  • public(公共):不受限制

  • private(私有):仅限于此类

  • internal(内部):仅限于此程序

  • protected(受保护):仅限于此类和从此类派生的类

  • protected internal:仅限于此程序和从此程序或此类派生的类

  • 类成员默认为private

字段和常量

字段的声明和访问

  • 声明:
    [字段修饰符] 类型 字段名 [= 初始化];

  • 访问:
    对象.字段名;

静态字段和实例字段

  • 是否使用static修饰符声明

  • 一个静态字段只标识一个存储位置。静态字段是所有实例之间共享一个副本,又称为静态变量

  • 类的每个实例都包含了该类的所有实例字段的一个单独副本。实例字段属于特定的实例,又称为实例变量

常量字段

  • 是在编译时设置其值并且永远不能更改其值的字段

  • 常量是表示常量值的类成员

  • 常量的值在编译时计算

  • 常量是静态成员,但不能用static声明

  • [修饰符] const 类型 字段名 = 初始化;

  • public const int AGE = 60;

只读与可变字段

只读字段

  • 只能在声明字段时赋值或在类的构造函数内被赋值,在其他位置,只读字段的值不能更改

  • 在运行时确定,可以是任何类型

  • [修饰符] readonly 类型 字段名 [= 初始化];

可变字段

  • 不受编译器优化的限制,可以由多个同时执行的线程修改,可以确保该字段在任何时间呈现的都是最新的值

  • [修饰符] volatile 类型 字段名 [= 初始化];

方法

方法是与类相关的函数,C#的函数必须与类或结构相关

声明:

在这里插入图片描述

调用:
对象.方法名([实参列表]);

基于表达式声明方法(C# 6.0)

在这里插入图片描述

【例7.10】基于表达式的声明方法示例(MethodExpression.cs):定义一个简单的SimpleMath1类,实现整数相加、求整数的平方、显示运算结果等操作

class SimpleMathTest
  {
    public static void Main()
    {
        int result; SimpleMath obj=new SimpleMath();
        result=obj.AddTwoNumbers(1, 2);  //两数相加
        SimpleMath.DisplayResult(result);  //显示相加结果
        SimpleMath.DisplayResult(obj.SquareANumber(result)); //显示某数的平方
        Console.ReadKey();
    }
  }
class SimpleMath
  {
    public int AddTwoNumbers(int number1, int number2) //两数相加
    {
        return number1 + number2;
    }
    public int SquareANumber(int number) //求某数的平方
    {
        return number * number;
    }
    public static void DisplayResult(int number) //显示结果
    { 
        Console.WriteLine("结果为:{0}", number);
    }
  }

方法:参数的传递

  • 方法的声明可以包含一个[形参列表]

  • 方法调用时则通过传递[实参列表]

  • 值形参(value parameter),声明时不带任何修饰符

  • 引用形参(reference parameter),用 ref 修饰符声明

  • 输出形参(output parameter),用 out 修饰符声明

  • 可选参数:指定参数的默认值

  • 形参数组(parameter array),用 params 修饰符声明

  • 命名参数(named arguments)

值形参与引用形参

  • 声明时不带修饰符的形参是值形参,用于输入参数的传递
  • 用ref修饰符声明的形参是引用形参,用于输入和输出参数的传递
static void Swap(int x, int y)  // 两数交换(值形参)
{ 
    int temp = x; x = y; y = temp; 
} 
static void Main() 
    { 
        int i = 1, j = 2; 
        Console.WriteLine("Before swap, i = {0}, j = {1}", i, j);
        Swap(i, j); 
        Console.WriteLine("After swap, i = {0}, j = {1}", i, j); } 
static void Swap(ref int x, ref int y)   // 两数交换(引用形参)
{
        int temp = x; x = y; y = temp; 
} 
static void Main() 
    {   
        int i = 1, j = 2; 
        Console.WriteLine("Before swap, i = {0}, j = {1}", i, j);
        Swap(ref i, ref j); 
        Console.WriteLine("After swap, i = {0}, j = {1}", i, j); } 

值形参

  • 值形参对应于方法声明空间的局部变量
  • 初始值是方法调用所提供的相应实参
  • 方法体中,赋给值形参的值只影响方法声明空间的局部存储位置

引用参数

  • 为引用参数传递的实参必须是变量
  • 引用形参并不创建新的存储位置
  • 在方法中对参数的任何更改都将反映在该变量中
  • 方法的定义和调用都需加ref

输出形参

  • 用 out 修饰符声明的形参是输出形参,用于输出参数的传递

  • 当控制权传递回调用方法时,把输出值传递给相应的变量

  • 输出形参并不创建新的存储位置

  • 变量在作为输出形参传递之前,不需要明确赋值

  • 将变量作为输出形参传递的调用之后,必须明确赋值

static void SplitPath(string path, out string dir, out string name) 
    {int i = path.Length; 
        while (i > 0) 
        { 
            char ch = path[i-1]; 
            if (ch == '\\' || ch == '/' || ch == ':') break; 
            i--; 
        } 
        dir = path.Substring(0, i); name = path.Substring(i); //目录、文件
    } 
    static void Main() 
    { 
        string dir, name; 
        SplitPath("c:\\Windows\\System\\hello.txt", out dir, out name); 
        Console.WriteLine("目录 = {0},文件名 = {1}",dir, name); 
    } 

可选参数

  • 指定参数的默认值

  • 声明方法时,有默认值的可选参数必须位于其他无默认值的参数之后

  • ref和out参数不能指定默认值

  • public static void Display(int a, double b = 2, int c =6)

  • Display(1)等价于Display(1,2,6)

形参数组

  • 用 params 修饰符声明,允许向方法传递可变数量的实参

  • 形参数组必须位于该列表的最后,且必须是一维数组类型
    不能与 ref 和 out 修饰符组合起来使用

  • static void F(params int[] args)

命名参数

方法调用时,可使用命名参数,即参数名称来传递参数

参数传递的意义更明确
可以不按形参参数定义的顺序
结合可选参数,可以简化调用

public static void Display(int a, double b = 2, int c =6)

Display(b:1,c:2,a:3)等价于Display(3,1,2)

重载

  • 定义两种或多种具有相同名称的方法,只要签名不同

  • 无法重载的情形:

  • 一个方法采用ref参数,而另一个方法采用out参数

    public void SampleMethod(double i) 
    {
        Console.WriteLine("SampleMethod(double i):{0}", i);
    }
    public void SampleMethod(int i) 
    {
        Console.WriteLine("SampleMethod(int i):{0}", i);
    }
    public void SampleMethod(ref int i) 
    {
        Console.WriteLine("SampleMethod(ref int i):{0}", i);
    }
    //public void SampleMethod(out int i) { }    //编译错误

静态与实例方法

  • 是否使用static修饰符声明

  • 静态方法只能直接访问静态成员

  • 静态方法中引用this会导致编译时错误

  • 静态方法通过类来访问

  • 实例方法能够访问静态成员和实例成员

  • 实例方法可以通过this显式地访问该实例

  • 实例方法通过类的实例来访问

分部方法

  • 使用partial修饰符定义分部方法
  • 分部方法的定义与实现在分部类的不同部分中声明
  • 两个声明必须具有相同的修饰符、类型、方法名、形参数列表
partial class Customer
    {
        string name;
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        partial void OnNameChanging(string newName);  
        //声明分部方法定义
        partial void OnNameChanged();               
         //声明分部方法定义
    }
partial class Customer
    {
        partial void OnNameChanging(string newName)       
        //声明分部方法实现
        {
            Console.WriteLine("Changing " + name + " to " + newName);
        }
        partial void OnNameChanged()               
       //声明分部方法实现
        {
            Console.WriteLine("Changed to " + name);
        }
}

外部方法

  • 方法声明包含 extern 修饰符时,为外部方法

  • 外部方法是在外部实现的(通常为dll库函数)

  • 外部方法声明不提供实际实现,其方法体只由一个分号组成

  • 不可以是泛型

  • extern 修饰符通常与DllImport特性一起使用,以引用由DLL(动态链接库)实现的外部函数

  • 当外部方法包含 DllImport特性时,该方法声明必须同时包含一个 static 修饰符

[DllImport(“kernel32”, SetLastError = true)]
public static extern int GetCurrentDirectory(int bufSize, StringBuilder buf);

递归方法

  • 函数调用自身的过程

  • 设计一个递归过程时,必须至少测试一个可以终止此递归的条件,并且还必须对在合理的递归调用次数内未满足此类条件的情况进行处理

  public static int factorial(int n)
        {
            if (n <= 1)   //终止递归
            { 
                return 1;
            }
            else        //递归调用
            {
                return factorial(n - 1) * n;
            }
        }

迭代器方法

  • 迭代器方法用于依次返回每个元素,一般用于foreach循环语句

  • 迭代器方法使用yield return语句,以保持代码的当前位置,在下一次调用迭代器方法时,从上一次的位置继续执行

返回类型一般为:
System.Collections.Generic.IEnumerable<int>
System.Collections. IEnumerable

迭代器对象

  • 使用foreach循环语句进行迭代的对象
  • 使用迭代器可以实现对象的迭代循环,迭代器让程序更加通用、优雅、高效
 DaysOfTheWeek days = new DaysOfTheWeek();
 foreach (string d in days) Console.Write("{0} ", d);

public class DaysOfTheWeek : IEnumerable
{
        private string[] days = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
        public IEnumerator GetEnumerator()
        {
            for (int i = 0; i < days.Length; i++) yield return days[i];
        }
}

属性

  • 一种用于访问对象或类的特性的成员

  • 字段的自然扩展,但属性不表示存储位置

  • 属性的读写一般与私有的字段紧密关联

  • 声明:
    在这里插入图片描述

  • 访问: 对象.属性名

get和set访问器

  • get访问器
    相当于一个具有属性类型返回值的无参数方法
    必须用return来返回一个可隐式转换为属性类型的表达式

  • set访问器
    相当于一个具有单个属性类型隐式值参数(始终命名为value)和void返回类型的方法

  • 读写属性(get和set)、只读属性(get)、只写属性(set)

例子

class TimePeriod
  {
    private double seconds;
    public double Hours
    {   
        get { return seconds / 3600; }       
       //秒转换为小时
        set
        {
            if (value > 0)
                seconds = value * 3600;  
              //小时转换为秒
            else
                Console.WriteLine("Hours的值不能为负数");
        }
    }
  }
  class Program
  {
    static void Main()
    {
        TimePeriod t = new TimePeriod();
        t.Hours = -6;         //调用set访问器
        t.Hours = 6;          //调用set访问器
        //调用get访问器
        Console.WriteLine("以小时为单位的时间: " + t.Hours);
        Console.ReadKey();
    }
  }

静态属性和实例属性

是否使用static修饰符声明

自动实现的属性

[属性修饰符] 类型 属性名{get;set;}[;]

当声明自动实现的属性时,编译器将创建一个私有的匿名后备字段,该字段只能通过属性的 get 和 set 访问器进行访问

public class Point
{
    public int X{get; set;}
    public int Y{get; set;}
}  
public class Point
{
    private int x;
    private int y;
public int X
{
get{return x}; 
set{x=value;};
}
public int Y
{
get{return y}; 
set{y=value;};
}
}  

索引器

  • 主要便于访问对象中封装的内部集合或数组

  • 允许对象像数组一样进行索引,并通过索引来操作对象的元素

  • 索引器与属性类似,又被称为带参数的属性

  • 索引器的名称固定为关键字this,且必须指定索引的参数表

[修饰符] 类型 this [参数表] 
{
[get {get访问器体 }]
[set {set访问器体 }]
}[;]

对象[索引参数]

class TempRecord
  { // 温度数组
    private float[] temps = new float[5] { 20.1F, 20.2F, 21.5F, 26.9F, 26.8F};
    public int Length                //属性
    {
        get { return temps.Length; }   
       //返回数组长度
    }
    public float this[int index]        
    //索引器
    {
        get { return temps[index]; }   
//返回指定索引所对应的数组元素
        set { temps[index] = value; }  
//设置指定索引所对应的数组元素的值
    }
  }
  class MainClass
  {
    static void Main()
    {
        TempRecord tempRecord = new TempRecord();
tempRecord[3] = 26.3F; 
tempRecord[4] = 62.1F;  //访问索引器
        //输出温度数组各元素的值
 for (int i = 0; i < tempRecord.Length; i++) 
Console.WriteLine("Element #{0} = {1}", i, tempRecord[i]);
        Console.ReadKey();
    }
  }

索引器的重载

  • 声明索引器时可以指定不同参数表,从而定义多个索引器,即索引器的重载

  • 不局限于整数,也可以定义字符串的索引器

运算符重载

  • 运算符重载通过使用operator关键字定义静态成员函数实现:
  • [修饰符] static类型 operator 运算符(参数表)
    {
    转换代码体
    }[;]

在这里插入图片描述

    public int real;           // 实部
    public int imaginary;      // 虚部

    public Complex(int real, int imaginary)  //构造函数
    {
        this.real = real;  this.imaginary = imaginary;
    }
    public static Complex operator +(Complex c1, Complex c2) // 重载运算符(+)
    {
        return new Complex(c1.real + c2.real, c1.imaginary + c2.imaginary);
    }

转换运算符

用于类或结构与其他类或结构或者基本类型之间进行相互转换

在这里插入图片描述

public static explicit operator Fahrenheit(Celsius c) //显式强制转换
    {   // 摄氏温度转换为华氏温度
        return new Fahrenheit((9.0f / 5.0f) * c.degrees + 32);
    }
public static implicit operator Celsius(Fahrenheit f) //隐式自动转换
    {   // 华氏温度转换为摄氏温度
        return new Celsius((5.0f / 9.0f) * (f.degrees - 32));
    }
Fahrenheit f = new Fahrenheit(100.0f);
Celsius c = f;            //隐式自动转换
Fahrenheit f2 = (Fahrenheit)c;    //显式强制转换

构造函数和析构函数

  • 实例构造函数用于执行类的实例的初始化工作

  • 创建对象时,根据传入的参数列表,将调用相应的构造函数

  • 每个类都有构造函数,如果没有显式声明构造函数,则编译器会自动生成一个默认的构造函数(无参数)

  • 默认构造函数实例化对象,并将未赋初值的字段设置为默认值

构造函数声明:
[修饰符] 类名 ([参数列表]) 
{
构造函数方法体
}[;]

构造函数特征:

  • 构造函数的名称与类名相同

  • 可以创建多个构造函数,以根据不同的参数列表进行相应的初始化

  • 不能声明返回类型(也不能使用void),也不能返回值

  • 一般构造函数总是public 类型的。private 类型的构造函数表明类不能被实例化,通常用于只含有静态成*员的类

  • 创建对象时,自动调用对应的构造函数,不能显式调用构造函数

  • 在构造函数中不要做对类的实例进行初始化以外的事情

class CoOrds        // 平面坐标
  {
    public int x, y;
    // 默认构造函数
    public CoOrds()      //默认构造函数
    {
        x = 0; y = 0;
    }
    // 有2个参数的构造函数:
    public CoOrds(int x, int y) //带2个参数的构造函数
    {
        this.x = x; this.y = y;
    }
}
public class Counter
  {
    private Counter() { }   //私有构造函数:阻止被实例化
    public static int currentCount;
    public static int IncrementCount()
    {
        return ++currentCount;
    }
  }

静态构造函数

  • 用于实现初始化类(而不是初始化实例或对象)所需的操作
  • 初始化任何静态数据,或执行仅需执行一次的特定操作
static 类名()
{
     构造函数方法体
}[;]

析构函数

用于实现销毁类的实例所需的操作,如释放对象占用的非托管资源(例如:打开的文件、网络连接等)

  • 在创建第一个实例或引用任何静态成员之前,自动调用静态构造函数
  • 类的静态构造函数在给定程序中至多执行一次
~类名() 
{
析构函数方法体
}[;]

析构函数特点:

  • 析构函数的名称由类名前面加上“~”字符构成

  • 析构函数既没有修饰符,没有返回值类型(甚至也不能使用void),也没有参数

  • 无法继承或重载析构函数,一个类只能有一个析构函数

  • 不能显式调用析构函数

  • 可以认为析构函数是构造函数的相反操作

  • 析构函数隐式地调用对象基类的Finalize(终结)方法,即对继承链递归调用 Finalize 方法。故不应使用空析构函数

嵌套类

  • 在类的内部还可以定义其他的类
  • 作为类的成员声明的类称为内部类或嵌套类
class Container
{
    public class Nested  //嵌套类
    {
      public void SayHello()
      {
         Console.WriteLine("Hello, I am a nested class!");
      }
    }
}
  • 在内部类内,this不能用于引用包含它的那个类的实例成员,只能引用内部类自己的成员
  • 内部类可以访问包含它的那个类可访问的所有成员
class Container
{
    string name = "Container";
    public void sayHello()
    { // 构造内部类实例时,传入包含内部类的类的this实例
        Nested n = new Nested(this);  n.sayHello();
    }
    public class Nested
    {
        Container c_parent;        // 用于保存外部类的实例
        public Nested(Container parent) // 构造函数
        {
            c_parent = parent;
        }
        public void sayHello()
        {
            Console.WriteLine(c_parent.name);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

醉卧考场君莫笑

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

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

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

打赏作者

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

抵扣说明:

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

余额充值