类成员的基本组成
字段和常量
方法
属性
索引器
运算符重载
构造函数和析构函数
嵌套类
必须通过类名来引用
一个静态字段共享同一个存储位置,只有一个副本
静态函数成员属于类的成员,在其代码体内不能直接引用实例成员
必须通过对象实例来引用
实例字段属于类的实例,每个实例分别包含各实例字段的单独副本
实例函数成员作用于类的给定实例,在其代码体内既可以使用实例成员,也可以直接引用类的静态成员
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.实例字段
public(公共):不受限制
private(私有):仅限于此类
internal(内部):仅限于此程序
protected(受保护):仅限于此类和从此类派生的类
protected internal:仅限于此程序和从此程序或此类派生的类
类成员默认为private
声明:
[字段修饰符] 类型 字段名 [= 初始化];
访问:
对象.字段名;
是否使用static修饰符声明
一个静态字段只标识一个存储位置。静态字段是所有实例之间共享一个副本,又称为静态变量
类的每个实例都包含了该类的所有实例字段的一个单独副本。实例字段属于特定的实例,又称为实例变量
是在编译时设置其值并且永远不能更改其值的字段
常量是表示常量值的类成员
常量的值在编译时计算
常量是静态成员,但不能用static声明
[修饰符] const 类型 字段名 = 初始化;
public const int AGE = 60;
只能在声明字段时赋值或在类的构造函数内被赋值,在其他位置,只读字段的值不能更改
在运行时确定,可以是任何类型
[修饰符] readonly 类型 字段名 [= 初始化];
不受编译器优化的限制,可以由多个同时执行的线程修改,可以确保该字段在任何时间呈现的都是最新的值
[修饰符] volatile 类型 字段名 [= 初始化];
方法是与类相关的函数,C#的函数必须与类或结构相关
声明:
调用:
对象.方法名([实参列表]);
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)
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); }
用 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 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
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访问器
相当于一个具有属性类型返回值的无参数方法
必须用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();
}
}
声明索引器时可以指定不同参数表,从而定义多个索引器,即索引器的重载
不局限于整数,也可以定义字符串的索引器
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!");
}
}
}
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);
}
}
}