标签:状态 因此 另一个 -- row int 开始 语义 des
前两章阐述了9种类成员中的两种:字段和方法。本章将会介绍除事件(第14章)和运算符外的其他类成员,并讨论其特征。

字段和方法的声明可以包括许多如public、private这样的修饰符。本章还会讨论许多其他修饰符。多个修饰符一起使用时,它们需要怎么排序呢?
[特性] [修饰符] 核心声明
例如,public和static都是修饰符,可以用在一起修饰某个声明。因为它们都是修饰符,所以顺序任意。下面两行代码是语义等价的:
public static int MaxVal; static public int MaxVal;

类的每个实例拥有自己的各个类成员的副本,这些成员称为实例成员。
改变一个实例字段的值不会影响任何其他实例成员中的值。
例
class D
{
public int Mem1;
}
class Progarm
{
static void Main()
{
D d1=new D();
D d2=new D();
d1.Mem1=10;
d2.Mem1=28;
Console.WriteLine("d1={0},d2={1}",d1.Mem1,d2.Mem2);
}
}


除了实例字段,类还可以拥有静态字段。
class D
{
int Mem1; //实例字段
static int Mem2; //静态字段
}
例:静态字段演示
class D
{
int Mem1;
static int Mem2;
...
}
static void Main()
{
D d1=new D();
D d2=new D();
...
}

静态成员可以使用点运算符从类的外部访问。但因为没有实例,所以必须使用类名。
类名 ↓ D.Mem2=5; ↑ 成员名
class D
{
int Mem1;
static int Mem2;
public void SetVars(int v1,int v2)
{
Mem1=v1;
Mem2=v2;
}
public void Display(string str)
{
Console.WriteLine("{0}:Mem1={1},Mem2={2}",str,Mem1,Mem2);
}
}
class Program
{
static void Main()
{
D d1=new D(),d2=new D();
d1.SetVars(2,4);
d1.Display("d1");
d2.SetVars(15,17);
d2.Display("d2");
d1.Display("d1");
}
}

class D
{
int Mem1;
static int Mem2;
...
}
static void Main()
{
D.Mem2=5;
Console.WriteLine("Mem2={0}",D.Mem2);
}

字段与类有关,与实例无关

静态成员即使没有类的实例也存在。如果静态字段有初始化语句,那么会在使用该类的任何静态成员之前初始化该字段,但没必要在程序执行的开始就初始化。
例:静态函数与静态字段
class X
{
static public int A;
static public void PrintValA()
{
Console.WriteLine("Value of A:{0}",A);
}
}
class Program
{
static void Main()
{
X.A=10;
X.PrintValA();
}
}


下表中为可以声明为static的类成员类型做了√标记

成员常量类似本地常量,只是它被声明在类声明中而不是方法内。
class MyClass
{
const int IntVal=100;//定义值为100的int类型常量
}
const double PI=3.1416; //错误:不能在类型声明之外
与本地常量类似,初始化成员常量的值在编译时必须是可计算的。
class MyClass
{
const int IntVal1=100;
const int IntVal2=2*IntVal1;//正确:因为IntVal的值在前一行已设置
}
与本地常量类似,不能在成员常量声明后给它赋值
class MyClass
{
const int IntVal//错误:声明时必须初始化
IntVal=100; //错误:不允许赋值
}
与C和C++不同,在C#中没有全局变量。每个常量都必须声明在类型内。
成员常量比本地常量更有趣,因为它们表现得像静态值。它们对类的每个实例都是“可见的”,而且即使没有类的实例也可以用。与真正的静态量不同,常量没有自己的存储位置,而是在编译时被编译器替换。常量类似C和C++中的#define。
正因为常量在内存没有存储位置,所以它也不能作为左值(被赋值)。
static静态量是有自己的存储位置的。
例:常量示例
class X
{
public const double PI=3.1416;
}
class Program
{
static void Main()
{
Console.WriteLine("pi={0}",X.PI);
}
}


虽然常量成员表现得像一个静态量,但不能将常量声明为static
static const double PI=3.14;//错误:不能将常量声明为static
属性代表类的实例或类中的一个数据项成员。使用属性看起来像写入或读取一个字段,它们语法相同。
与字段类似,属性有如下特征
然而和字段不同,属性是一个函数成员
属性是指定的一组两个匹配的、称为访问器的方法

访问器的其他重点
例:名为C1的类,包含一个名为MyValue的属性
class C1
{
private int TheRealValue;//字段:分配内存
public int MyValue //属性:不分配内存
{
set
{
TheRealValue=value;
}
get
{
return TheRealValue;
}
}
}

int MyValue
{
get{...}
set{...}
}
...
MyValue=5;
z=MyValue;
正如下文C1示例中的。一种常见的方式是在类中将字段声明为private以封装字段,并声明一个public属性来控制从类的外部对该字段的访问。
和属性关联的字段常被称为后备字段、后备存储。
例:使用public的MyValue来控制对private的TheRealValue的访问
class C1
{
private int TheRealValue=10;//字段:分配内存
public int MyValue //属性:不分配内存
{
set{TheRealValue=value;}
get{return TheRealValue;}
}
}
class Program
{
static void Main()
{
C1 c=new C1();
Console.WriteLine("MyValue:{0}",c.MyValue);
c.MyValue=20;
Console.WriteLine("MyValue:{0}",c.MyValue);
}
}
属性和它后备字段的命名有两种约定。
约定一:属性使用Pascal大小写,字段使用Camel大小写。虽然这违反了“仅使用大小写区分不同标识符是坏习惯”。但胜在简单,有意义。
约定二:属性使用Pascal大小写,字段使用Camel大小写并在开头加"_"号。
private int firstField;
public int FirstField
{
get{return firstField;}
set{firstField=value;}
}
private int _secondField;
public int SecondField
{
get{return _secondField;}
set{_secondField=value;}
}
属性访问器不仅对后备字段传进传出数据。也可以执行任何计算。
例:通过set属性访问器限制Hour的最大值为24
int Hour=12;
int MyValue
{
set
{
Hour=value>24?24:value;
}
get
{
return Hour;
}
}
上面示例中,演示的条件运算符,将在第8章详细阐述。
条件运算符是一种三元运算符,计算问号前的表达式,如果表达式结果为true,则返回问号后第一个表达式,否则,返回冒号后的表达式。

按照推荐的编码实践,属性比公共字段更好
例:类RightTriangle(直角三角形)的只读属性Hypotenuse(斜边)
class RightTriangle
{
public double A=3;
public double B=4;
public double Hypotenuse
{
get{return Math.Sqrt((A*A)+(B*B));}
}
}
class Program
{
static void Main()
{
var c=new RightTriangle();
Console.WriteLine("Hypotenuse:{0}",c.Hypotenuse);
}
}

因为属性经常关联到后备字段,C#提供了自动实现属性(automatically implemented property),允许只声明属性而不声明后备字段。编译器为你创建隐藏的后备字段,并且字段挂接到get和set访问器上。
自动属性的要点如下
例:自动属性
class C1
{
public int MyValue //属性:分配内存
{
set;get;
}
}
class Program
{
static void Main()
{
C1 c=new C1();
Console.WriteLine("MyValue:{0}",c.MyValue);
c.MyValue=20;
Console.WriteLine("MyValue:{0}",c.MyValue);
}
}

除方便以外,自动属性使你在倾向于使用公有字段的地方很容易用属性将其替代。
属性也可以声明为static。静态属性的访问器和静态成员一样,具有以下特点
例:静态属性
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}",Trival.MyValue);
Trival.MyValue=10;
Console.WriteLine("New Value:{0}",Trival.MyValue);
var tr=new Trivial();
tr.PrintValue();
}
}

实例构造函数是一个特殊的方法,它在创建类的每个新实例时执行。
class MyClass
{ 和类名相同
↓
public MyClass()
{ ↑
没有返回类型
...
}
}
例:使用构造函数初始化TimeOfInstantiation字段为当前时间
class MyClass
{
DateTime TimeOfInstantiation;
...
public MyClass()
{
TimeOfInstantiation=DateTime.Now;
}
...
}
在学完静态属性后,我们可以仔细看看初始化TimeOfInstantiation那一行。DateTime类(实际上它是一个结构,但由于还没介绍结构,你可以先把它当成类)是从BCL中引入的,Now是类DateTime的静态属性。Now属性创建一个新的DateTime类实例,将其初始化为系统时钟中的当前日期和时间,并返回新DateTime实例的引用。
例:有3个构造函数的Class
class Class1
{
int Id;
string Name;
public Class1(){Id=28;Name="Nemo";}
public Class1(int val){Id=val;Name="Nemo";}
public Class1(String name){Name=name;}
public void SoundOff()
{
Console.WriteLine("Name{0},Id{1}",Name,Id);
}
}
class Program
{
static void Main()
{
CLass1 a=new Class1(),
b=new Class1(7),
c=new Class1("Bill");
a.SoundOff();
b.SoundOff();
c.SoundOff();
}
}

如果在类的声明中没有显式的提供实例构造函数,那么编译器会提供一个隐式的默认构造函数,它有以下特征。
只要你声明了构造函数,编译器就不再提供默认构造函数。
例:显式声明了两个构造函数的Class2
class Class2
{
public Class2(int Value){...}
public Class2(string Value){...}
}
class Program
{
static void Main()
{
Class2 a=new Class2();//错误!没有无参数的构造函数
...
}
}
实例构造函数初始化类的每个新实例,static构造函数初始化类级别的项。通常,静态构造函数初始化类的静态字段。
class Class1
{
static Class1
{
...
}
}
关于静态构造函数还有其他要点
静态构造函数示例
class RandomNumberClass
{
private static Random RandomKey;
static RandomNumberClass()
{
RandomKey=new Random();
}
public int GetRandomNumber()
{
return RandomKey.Next();
}
}
class Program
{
static void Main()
{
var a=new RandomNumberClass();
var b=new RandomNumberClass();
Console.WriteLine("Next Random #:{0}",a.GetRandomNumber());
Console.WriteLine("Next Random #:{0}",b.GetRandomNumber());
}
}

对象初始化语句扩展了创建语法,允许你在创建新的对象实例时,设置字段和属性的值。

例:
new Point {X=5,Y=6};
public class Point
{
public int X=1;
public int Y=2;
}
class Program
{
static void Main()
{
var pt1=new Point();
var pt2=new Point(X=5,Y=6);
Console.WriteLine("pt1:{0},{1}",pt1.X,pt1.Y);
Console.WriteLine("pt2:{0},{1}",pt2.X,pt2.Y);
}
}

析构函数(destructor)执行在类的实例被销毁前需要的清理或释放非托管资源行为。非托管资源通过Win32 API获得文件句柄,或非托管内存块。使用.NET资源无法得到它们,因此如果坚持使用.NET类,就无需为类编写析构函数。
因此,我们等到第25章再描述析构函数。
字段可用readonly修饰。其作用类似于将字段声明为const,一旦值被设定就不能改变。
例:Shape类,两个readonly字段
class Shape
{
readonly double PI=3.1416;
readonly int NumberOfSides;
public Shape(double side1,double side2)
{
// 矩形
NumberOfSides=4;
...
}
public Shape(double side1,double side2,double side3)
{
// 三角形
NumberOfSides=3;
...
}
}
this关键字在类中使用,表示对当前实例的引用。它只能被用在下列类成员的代码块中。
静态成员不是实例的一部分,所以不能在静态函数成员中使用this。换句话说,this用于下列目的:
例:MyClass类,在方法内使用this关键字区分两个Var1
class MyClass
{
int Var1=10;
public int ReturnMaxSum(int Var1)
{ 参数 字段
↓ ↓
return Var1>this.Var1?Var1:this.Var1;
}
}
class Program
{
static void Main()
{
var mc=new MyClass();
Console.WriteLine("Max:{0}",mc.ReturnMaxSum(30));
Console.WriteLine("Max:{0}",mc.ReturnMaxSum(5));
}
}

假如我们定义一个Employee类,它带有3个string型字段,如果不用索引器,我们用字段名访问它们。
class Employee
{
public string LastName;
public string FirstName;
public string CityOfBirth;
}
class Program
{
static void Main()
{
var emp1=new Employee();
emp1.LaseName="Doe";
emp1.FirstName="Jane";
emp1.CityOfBirth="Dallas";
}
}

如果能使用索引访问它们将会很方便,好像该实例是字段的数组一样。
static void Main()
{
var emp1=new Employee();
emp1[0]="Doe";
emp1[1]="Jane";
emp1[2]="Dallas";
}

索引器是一组get和set访问器,与属性类似。
索引器和属性在很多方法类似
可以认为索引器是为类的多个数据成员提供get、set属性。通过索引器,可以在许多可能的数据成员中进行选择。索引器本身可以是任何类型。
关于索引器的注意事项
Return Type this [Type param1,...]
{
get{...}
set{...}
}
声明索引器类似于声明属性。

当索引器被用于赋值时,set访问器被调用,并接受两项数据
下图例表明set访问器有如下语义

get访问器方法体内的代码必须检查索引参数,确定它表示哪个字段,并返回字段值。
get访问器有如下语义

和属性一样,不能显示调用get、set访问器。取而代之,当索引器用在表达式中取值时,将自动调用get访问器。索引器被赋值时,自动调用set访问器。
在“调用”索引器时,要在方括号中提供参数。
索引 值
↓ ↓
emp[0]="Doe"; //调用set访问器
string NewName=emp[0]; //调用get访问器
下面代码为示例中的类Employee声明了一个索引器
class Employee
{
public string LastName;
public string FirstName;
public string CityOfBirth;
public string this[int index]
{
set
{
switch(index)
{
case 0:LaseName=value;
break;
case 1:FirstName=value;
break;
case 2:CityOfBirth=value;
break;
default:
throw new ArgumentOutOfRangeException("index");
}
}
get
{
switch(index)
{
case 0:return LaseName;
case 1:return FirstName;
case 2:return CityOfBirth;
default:throw new ArgumentOutOfRangeException("index");
}
}
}
}
例:为类Class1的两个int字段设置索引
class Class1
{
int Temp0;
int Temp1;
public int this[int index]
{
get
{
return(index==0?Temp0:Temp1;)
}
set
{
if(index==0){Temp0=value;}
else{Temp1=value;}
}
}
}
class Example
{
static void Main()
{
var a=new Class1();
Console.WriteLine("Values -- T0:{0},T1:{1}",a[0],a[1]);
a[0]=15;
a[1]=20;
Console.WriteLine("Values--T0:{0},T1:{1}",a[0],a[1]);
}
}

类可以有任意多个参数列表不同的索引器。(返回类型不同,不是重载)
例:下面示例有3个索引器
class Myclass
{
public string this[int index]
{
get{...}
set{...}
}
public string this[int index1,int index2]
{
get{...}
set{...}
}
public int this[float index1]
{
get{...}
set{...}
}
}
本章中,你已看到了两种带get、set访问器的函数成员:属性和索引器。默认情况下,成员的两个访问器的访问级别和成员自身相同。也就是说,如果一个属性有public访问级别,那么它的两个访问器也是public的。
不过,你可以为两个访问器分配不同访问级别。例如,下面代码演示了一个常见且重要的例子–set访问器声明为private,get访问器声明为public。(get之所以是public,是因为属性的访问级别就是public)
注意:在这段代码中,尽管可以从类的外部读取该属性,但却只能在类的内部设置它。这是非常重要的封装工具。
class Person
{
public string Name{get;private set;}
public Person(string name)
{
Name=name;
}
}
class Program
{
static public void Main()
{
var p=new Person("Capt,Ernest Evans");
Console.WriteLine("Person‘s name is {0}",p.Name);
}
}
访问器的访问修饰符有几个限制。最重要的限制如下。

例如,如果一个属性的访问级别是public,在图里较低的4个级别中,它的访问器可以使用任意一个。但如果属性的访问级别是protected,则其访问器唯一能使用的访问修饰符是private。
类的声明可以分割成几个分部类的声明
每个局部声明必须标为partial class,而不是class。分部类声明看起来和普通类声明相同。
类型修饰符partial不是关键字,所以在其他上下文中,可以把它用作标识符。但直接用在关键字class、struct或interface前时,它表示分部类型。
例:分部类

Visual Studio为标准的Windows程序模板使用了这个特性。当你从标准模板创建ASP.NET项目、Windows Forms项目或Windows Persentation Foudation(WPF)项目时,模板为每个Web页面、表单、WPF窗体创建两个类文件。
分部方法是声明在分部类中不同部分的方法。
分部方法的两个部分如下
关于分部方法需要了解的重要内容如下
下面是一个名为PrintSum的分部方法的示例
partial class MyClass
{
必须是void
↓
partial void PrintSum(int x,int y);//定义分部方法
public void Add(int x,int y)
{
PrintSum(x,y);
}
}
partial class MyClass
{
partial void PrintSum(int x,int y)//实现分部方法
{
Console.WriteLine("Sum i {0}",x+y);
}
}
class Program
{
static void Main()
{
var mc=new MyClass();
mc.Add(5,6);
}
}

from: http://www.cnblogs.com/moonache/p/6097402.html
标签:状态 因此 另一个 -- row int 开始 语义 des
原文地址:https://www.cnblogs.com/GarfieldEr007/p/10126556.html