C#语言参考--(1)介绍-2

来源:互联网 发布:英格拉姆2018赛季数据 编辑:程序博客网 时间:2024/05/21 12:08
1.7 类
类声明定义新的引用类型。一个类可以从其它类继承,并且可以没用接口或有多个接口。
类的成员可以包括常数、域、方法、属性、索引、事件、操作符、构造函数、析构器和嵌套类型声明。每个成员有相关的访问能力,这控制了可以访问这个成员的程序文本的区域。有访问能力有五种可能形式。在下表中进行总结。
形式 直观意义
public 访问不受限制
protected 访问只限于此程序或类中包含的类型
internal 访问只限于此程序
protected internal 访问只限于此程序或类中包含的类型
private 访问只限于所包含的类型

例子
using System;
class MyClass
{
 public MyClass() {
  Console.WriteLine("Constructor");
 }
 public MyClass(int value) {
  MyField = value;
  Console.WriteLine("Constructor");
 }
 ~MyClass() {
  Console.WriteLine("Destructor");
 }
 public const int MyConstant = 12;
 public int MyField = 34;
 public void MyMethod(){
  Console.WriteLine("MyClass.MyMethod");
 }
 public int MyProperty {
  get {
   return MyField;
  }
  set {
   MyField = value;
  }
 }
 public int this[int index] {
  get {
   return 0;
  }
  set {
   Console.WriteLine("this[{0}] was set to {1}", index, value);
  }
 }
 public event EventHandler MyEvent;
 public static MyClass operator+(MyClass a, MyClass b) {
  return new MyClass(a.MyField + b.MyField);
 }
 internal class MyNestedClass
 {}
}
介绍了一个包含每种类型成员的类。例子
class Test
{
 static void Main() {
  // Constructor usage
  MyClass a = new MyClass();
  MyClass b = new MyClass(123);
  // Constant usage
  Console.WriteLine("MyClass.MyConstant = {0}", MyClass.MyConstant);
  // Field usage
  a.MyField++;
  Console.WriteLine("a.MyField = {0}", a.MyField);
  // Method usage
  a.MyMethod();
  // Property usage
  a.MyProperty++;
  Console.WriteLine("a.MyProperty = {0}", a.MyProperty);
  // Indexer usage
  a[3] = a[1] = a[2];
  Console.WriteLine("a[3] = {0}", a[3]);
  // Event usage
  a.MyEvent += new EventHandler(MyHandler);
  // Overloaded operator usage
  MyClass c = a + b;
 }
 static void MyHandler(object sender, EventArgs e) {
  Console.WriteLine("Test.MyHandler");
 }
 internal class MyNestedClass
 {}
}
介绍如何使用这些成员。.
1.7.1 常数
一个常数是一个代表常数值的类成员:某个可以在编译时计算的数值。只要没有循环从属关系,允许常数依赖同一程序中的其它常数。例子
class Constants
{
 public const int A = 1;
 public const int B = A + 1;
}
包括一个名为Constants的类,有两个公共常数。
常数是隐式静态类型,可以通过类来访问,
class Test
{
 static void Main() {
  Console.WriteLine("A = {0}, B = {1}", Constants.A, Constants.B);
 }
}
在此例中打印输出Constants.A和Constants.B的数值。
1.7.2 域
域是一个代表和某对像或类相关的变量的成员。例子
class Color
{
 internal ushort redPart;
 internal ushort bluePart;
 internal ushort greenPart;
 public Color(ushort red, ushort blue, ushort green) {
  redPart = red;
  bluePart = blue;
  greenPart = green;
 }
 ...
}
介绍了一个Color类,它有局部的实例域,分别叫做redPart、greenPart和bluePart。域可以是静态的,在下面的例子中
class Color
{
 public static Color Red = new Color(0xFF, 0, 0);
 public static Color Blue = new Color(0, 0xFF, 0);
 public static Color Green = new Color(0, 0, 0xFF);
 public static Color White = new Color(0, 0, 0);
 public static Color Black = new Color(0xFF, 0xFF, 0xFF);
 ...
}
介绍了Red、Blue、Green、White和Black的静态域。
静态域在这里并不太合适。当Color类被加载后,域就被初始化了,但是在初始化之后,并不能阻止用户改变它们。而那样的改动可能会引起别的使用Color并认为数值不会变的程序的不可预见的错误。只读域可以用来避免这一错误的发生。对于一个只读域的赋值,只会在相同类中的部分声明和构造函数中发生。这样,就可以通过给静态域添加只读修饰符来增强Color类:
class Color
{
 internal ushort redPart;
 internal ushort bluePart;
 internal ushort greenPart;
 public Color(ushort red, ushort blue, ushort green) {
  redPart = red;
  bluePart = blue;
  greenPart = green;
 }
 public static readonly Color Red = new Color(0xFF, 0, 0);
 public static readonly Color Blue = new Color(0, 0xFF, 0);
 public static readonly Color Green = new Color(0, 0, 0xFF);
 public static readonly Color White = new Color(0, 0, 0);
 public static readonly Color Black = new Color(0xFF, 0xFF, 0xFF);
}
1.7.3 方法
方法是一个执行可以由对像或类完成的计算或行为的成员。方法有一个形式参数列表(可能为空),一个返回数值(或void),并且可以是静态也可以是非静态。静态方法要通过类来访问。非静态方法,也称为实例方法,通过类的实例来访问。例子
using System;
public class Stack
{
 public static Stack Clone(Stack s) {...}
 public static Stack Flip(Stack s) {...}
 public object Pop() {...}
 public void Push(object o) {...}
 public override string ToString() {...}
 ...
}
class Test
{
 static void Main() {
  Stack s = new Stack();
  for (int i = 1; i < 10; i++)
   s.Push(i);
  Stack flipped = Stack.Flip(s);
  Stack cloned = Stack.Clone(s);
  Console.WriteLine("Original stack: " + s.ToString());
  Console.WriteLine("Flipped stack: " + flipped.ToString());
  Console.WriteLine("Cloned stack: " + cloned.ToString());
 }
}
介绍了Stack,它有许多静态方法(Clone和Flip)和许多实例方法(Push、Pop和ToString)。
方法可以被重复调用,这意味着只要有一个唯一的签名,多个方法可能有相同的名称。方法的签名包括方法、数据、修饰符和它的形式参数的各种类型的名称。方法的签名不包括return类型。例子
class Test
{
 static void F() {
  Console.WriteLine("F()");
 }
 static void F(object o) {
  Console.WriteLine("F(object)");
 }
 static void F(int value) {
  Console.WriteLine("F(int)");
 }
 static void F(int a, int b) {
  Console.WriteLine("F(int, int)");
 }
 static void F(int[] values) {
  Console.WriteLine("F(int[])");
 }
 static void Main() {
  F();
  F(1);
  F((object)1);
  F(1, 2);
  F(new int[] {1, 2, 3});
 }
}
介绍了有一个成员方法F的类。程序的输出为
F()
F(int)
F(object)
F(int, int)
F(int[])
1.7.4 属性
属性是提供对对像或类的特性进行访问的成员。属性的例子包括字符串的长度,字体的大小,窗口的焦点,用户的名字,等等。属性是域的自然扩展。两者都是用相关类型成员命名,并且访问域和属性的语法是相同的。然而,与域不同,属性不指示存储位置。作为替代,属性有存取程序,它指定声明的执行来对他们的进行读或写。
属性是由属性声明定义的。属性声明的第一部分看起来和域声明相当相似。第二部分包括一个get存取程序和一个set存取程序。在下面的例子类Button定义了一个Caption属性。
public class Button
{
 private string caption;
 public string Caption {
  get {
   return caption;
  }
  set {
   caption = value;
   Repaint();
  }
 }
}
像Caption属性一样的读写都可以的属性包括get和set存取程序。当属性的值要被读出的时候,会调用get存取程序;当要写属性值的时候,会调用set存取程序。 Properties 在set存取程序中,属性的新值赋给一个名为value的隐含参数。
属性的声明是相对直接了当的,但是属性显式它自己的数值是在使用的时候而不是在声明的时候。可以按照对域进行读写的方法来读写Caption属性:
Button b = new Button();
b.Caption = "ABC";  // set
string s = b.Caption; // get
b.Caption += "DEF”;  // get & set
1.7.5 事件
事件是使得对像和类提供通知的成员。一个类通过提供事件声明来定义一个事件,这看起来与域和事件声明相当类似,但是有一个event关键字。这个声明的类型必须是delegate类型。
在这个例子中
public delegate void EventHandler(object sender, Event e);
public class Button
{
 public event EventHandler Click;
 public void Reset() {
  Click = null;
 }
}
Button类定义了一个类型为EventHandler的Click事件。在Button类中,Click成员与一个EventHandler类型的私有域相对应。然而,在Button类外,Click成员只能用在+=和-=操作符的左边。这在添加和删除事件句柄方面限制客户代码。例子
using System;
public class Form1
{
 public Form1() {
  // Add Button1_Click as an event handler for Button1’s Click event
  Button1.Click += new EventHandler(Button1_Click);
 }
 Button Button1 = new Button();
 void Button1_Click(object sender, Event e) {
  Console.WriteLine("Button1 was clicked!");
 }
 public void Disconnect() {
  Button1.Click -= new EventHandler(Button1_Click);
 }
}
介绍了类Form1,它为Button1的Click事件添加了Button1_Click作为事件句柄。在Disconnect方法中,去掉了事件句柄。
如例子中所示,类Button需要被重写来使用像属性一样的事件声明而不是像域一样的事件声明。
public class Button
{
  public event EventHandler Click {
   get {...}
   set {...}
  }
  public void Reset() {
   Click = null;
  }
}
这个改变不会影响到客户代码,但是因为Click的事件句柄不需要用域来实现,所以允许类Button的执行更灵活。
1.7.6 操作符
操作符是一个定义了可以用来使用在类的实例上的表达式操作符所代表的意义的对象。这里有三种可以定义的操作符:一元操作符,二元操作符和转换操作符。
下面的例子定义了Digit类型,它可以描述0到9间的小数-整数值。
using System;
public struct Digit
{
 byte value;
 public Digit(byte value) {
  if (value < 0 || value > 9) throw new ArgumentException();
  this.value = value;
 }
 public Digit(int value): this((byte) value) {}
 public static implicit operator byte(Digit d) {
  return d.value;
 }
 public static explicit operator Digit(byte b) {
  return new Digit(b);
 }
 public static Digit operator+(Digit a, Digit b) {
  return new Digit(a.value + b.value);
 }
 public static Digit operator-(Digit a, Digit b) {
  return new Digit(a.value - b.value);
 }
 public static bool operator==(Digit a, Digit b) {
  return a.value == b.value;
 }
 public static bool operator!=(Digit a, Digit b) {
  return a.value != b.value;
 }
 public override bool Equals(object value) {
  return this == (Digit) value;
 }
 public override int GetHashCode() {
  return value.GetHashCode();
 }
 public override string ToString() {
  return value.ToString();
 }
}
class Test
{
 static void Main() {
  Digit a = (Digit) 5;
  Digit b = (Digit) 3;
  Digit plus = a + b;
  Digit minus = a – b;
  bool equals = (a == b);
  Console.WriteLine("{0} + {1} = {2}", a, b, plus);
  Console.WriteLine("{0} - {1} = {2}", a, b, minus);
  Console.WriteLine("{0} == {1} = {2}", a, b, equals);
 }
}
Digit类型定义了下面的操作符:
• 从Digit到byte的隐式转换操作符。
• 从byte到Digit的隐式转换操作符
• 把两个Digit数值相加并返回一个Digit数值的加法操作符。
• 把一共Digit数值与其它Digit数值相减,并返回一共digit数值的减法操作符。
• 比较两个digit数值的等式和非等式。
1.7.7 索引
索引(indexer)是使得对象可以像数组一样被索引的成员。然而属性使类似域的访问变得可能,索引使得类似数组的访问变得可能。
作为一个例子,考虑前面给出的类Stack。这个类会需要执行类似数组的访问,所以可能会不通过执行不需要的Push和Pop操作而检查或改变堆栈中的项目。Stack的构造像个列表,但是需要提供方便的数组存取。
索引的声明类似于属性的声明,最大的不同在于索引是无名的(由于this是被索引,所以用于声明中的名称是this)而且索引包含索引参数。索引参数在方括号中提供。例子
using System;
public class Stack
{
 private Node GetNode(int index) {
  Node temp = first;
  while (index > 0) {
   temp = temp.Next;
   index--;
  }
  return temp;
 }
 public object this[int index] {
  get {
   if (!ValidIndex(index))
    throw new Exception("Index out of range.");
   else
    return GetNode(index).value;
  }
  set {
   if (!ValidIndex(index))
    throw new Exception("Index out of range.");
   else
    GetNode(index).value = value;
  }
 }
 ...
}
class Test
{
 static void Main() {
  Stack s = new Stack();
  s.Push(1);
  s.Push(2);
  s.Push(3);
  s[0] = 33; // Changes the top item from 3 to 33
  s[1] = 22; // Changes the middle item from 2 to 22
  s[2] = 11; // Changes the bottom item from 1 to 11
 }
}
介绍了一个Stack中的索引
1.7.8 实例构造函数
实例构造函数是实现对类中实例进行初始化的行为的成员。
例子
using System;
class Point
{
 public double x, y;
 public Point() {
  this.x = 0;
  this.y = 0;
 }
 public Point(double x, double y) {
  this.x = x;
  this.y = y;
 }
 public static double Distance(Point a, Point b) {
  double xdiff = a.x – b.x;
  double ydiff = a.y – b.y;
  return Math.Sqrt(xdiff * xdiff + ydiff * ydiff);
 }
 public override string ToString() {
  return string.Format("({0}, {1})", x, y);
 }
}
class Test
{
 static void Main() {
  Point a = new Point();
  Point b = new Point(3, 4);
  double d = Point.Distance(a, b);
  Console.WriteLine("Distance from {0} to {1} is {2}", a, b, d);
 }
}
介绍了一个类Point,它提供了两个公用的构造函数。一个没有参数的Point构造函数和一个有两个double参数的构造函数。
如果类中没有提供构造函数,那么会自动提供一个没有参数的构造函数。
1.7.9 析构函数
析构函数是实现破坏一个类的实例的行为的成员。析构函数不能有参数,不能任何修饰符而且不能被调用。析构函数在碎片收集时会被自动调用。
例子
using System;
class Point
{
 public double x, y;
 public Point(double x, double y) {
  this.x = x;
  this.y = y;
 }
 ~Point() {
  Console.WriteLine("Destructed {0}", this);
 }
 public override string ToString() {
  return string.Format("({0}, {1})", x, y);
 }
}
介绍了一个有析构函数的类Point。
1.7.10 静态构造函数
静态构造函数是实现对一个类进行初始化的行为的成员。静态构造函数不能有参数,不能有修饰符而且不能被调用,当类被加载时,类的静态构造函数自动被调用。
例子
using System.Data;
class Employee
{
 private static DataSet ds;
 static Employee() {
  ds = new DataSet(...);
 }
 public string Name;
 public decimal Salary;
 ...
}
介绍了一个有静态构造函数的类Employee,这个函数对静态域进行初始化。
1.7.11 继承
类支持单继承,object类型是所有类的基类。
前面所介绍的例子中的类都是隐含地从object派生而来的。例子
class A
{
 public void F() { Console.WriteLine("A.F"); }
}
介绍了从object派生出来的类A。例子
class B: A
{
 public void G() { Console.WriteLine("B.G"); }
}
class Test
{
 static void Main() {
  B b = new B();
  b.F();   // Inherited from A
  b.G();   // Introduced in B

  A a = b;   // Treat a B as an A
  a.F();
 }

介绍了从类A中派生出来的类B。类B继承了类A的方法F,并且创建了自己的方法G。
方法,属性和索引都可以是虚的,这意味着他们可以在派生的类中被重写。例子
using System;
class A
{
 public virtual void F() { Console.WriteLine("A.F"); }
}
class B: A
{
 public override void F() {
  base.F();
  Console.WriteLine("B.F");
 }
}
class Test
{
 static void Main() {
  B b = new B();
  b.F();
  A a = b;
  a.F();
 }

介绍了有虚方法F的类A,而类B替换了F。B中的替换方法包含了一个对A中被替换的方法的调用base.F()。
可以通过包括abstract修饰符来说明一个类是不完整的,只是用作其它类的基类。这样的类被称为抽象类。抽象类可以指定抽象函数-非抽象派生类必须实现的成员。例子
using System;
abstract class A
{
 public abstract F();
}
class B: A
{
 public override F() { Console.WriteLine("B.F"); }
}
class Test
{
 static void Main() {
  B b = new B();
  B.F();
  A a = b;
  a.F();
 }
}
介绍了抽象类A中的抽象类F,非抽象类B提供了对此方法的实现。