分享

C#的一些关键字的总结

 趋明 2012-03-29

C#的一些关键字的总结

今天突然有一种整理一下C#关键字的冲动,就转化为行动了!

C#关键字完整列表

abstract

as

base

bool

break

byte

case

catch

char

checked

class

const

continue

decimal

default

delegate

do

double

else

enum

ecent

explicit

extern

false

finally

fixed

float

for

foreach

get

goto

if

implicit

in

int

interface

internal

is

lock

long

namespace

new

null

object

out

override

partial

private

protected

public

readonly

ref

return

sbyte

sealed

set

short

sizeof

stackalloc

static

struct

switch

this

throw

true

try

typeof

uint

ulong

unchecked

unsafe

ushort

using

value

virtual

volatile

volatile

void

where

while

yield

 

 

 

 

 

 

 

 

其中有几个比较容易弄错的

关键字                                                            描      述

abstract       可以和类、方法、属性、索引器及事件一起使用,
                     标识一个可以扩展但不能被实体化的、必须被实现的类或方法。

as                一个转换操作符,如果转换失败,就返回null。 

base             用于访问被派生类或构造中的同名成员隐藏的基类成员。

catch             定义一个代码块,在特定类型异常抛出时,执行块内代码。
                     参见try和finally。

checked        既是操作符又是语句。
                      确保编译器运行时,检查整数类型操作或转换时出现的溢出。

const            标识一个可在编译时计算出来的变量值,即一经指派不可修改的值。

delegate        指定一个声明为一种委托类型。委托把方法封装为可调用实体,
                     能在委托实体中调用。

enum          表示一个已命名常量群集的值类型。

event           允许一个类或对象提供通知的成员,他必须是委托类型。

explicit         一个定义用户自定义转换操作符的操作符,
                    通常用来将内建类型转换为用户定义类型或反向操作。
                    必须再转换时调用显示转换操作符。

extern          标识一个将在外部(通常不是c#语言)实现的方法。


finally           定义一个代码块,在程序控制离开try代码快后执行。参见try和catch。


fixed            在一个代码块执行时,在固定内存位置为一个变量指派一个指针。


foreach         用于遍历一个群集的元素。


goto             一个跳转语句,将程序执行重定向到一个标签语句。

implicit          一个操作符,定义一个用户定义的转换操作符。
                     通常用来将预定义类型转换为用户定义类型或反向操作。
                     隐式转换操作符必须在转换时使用。

interface      将一个声明指定为接口类型,即实现类或构造必须遵循的合同。

internal        一个访问修饰符。

namespace   定义一个逻辑组的类型和命名空间。

operator       用来声明或多载一个操作符。

out               标识一个参数值会受影响的参数,但在传入方法时,
                    该参数无需先初始化。

params          声明一个参数数组。如果使用,必须修改指定的最后一个参数。
                     允许可选参数。

readonly        标识一个变量的值在初始化后不可修改。

ref               标识一个参数值可能会受影响的参数。

sealed            防止类型被派生,防止方法和property被覆载。

sizeof           一个操作符,以byte为单位返回一个值类型的长度。

stackalloc    返回在堆上分配的一个内存块的指针。

struct            struct是一种值类型,可以声明常量、字段、方法、property、
                    索引器、操作符、构造器和内嵌类型。

throw          抛出一个异常。

try              异常处理代码块的组成部分之一。try代码块包括可能会
                    抛出异常的代码。参阅catch和finally关键字。

typeof         一个操作符,返回传入参数的类型。

unchecked   禁止溢出检查。

unsafe         标注包含指针操作的代码块、方法或类。

using           当用于命名空间时,using关键字允许访问该命名空间中的类型,
                    而无需指定其全名。也用于定义finalization操作的范围。

virtual          一个方法修饰符,标识可被覆载的方法。

volatile         标识一个可被操作系统、某些硬件设备或并发线程修改的attribute。

 

整理完后发现时间还早,就干脆再深入一点

 

 

sealed 修饰符表示密封用于类时,表示该类不能再被继承,
不能和 abstract 同时使用,因为这两个修饰符在含义上互相排斥用于方法和属性时,
表示该方法或属性不能再被继承,必须和 override 关键字一起使用,因为使用 sealed 修饰符的方法或属性肯定是
基类中相应的虚成员通常用于实现第三方类库时不想被客户端继承,
或用于没有必要再继承的类以防止滥用继承造成层次结构体系混乱恰当的利用
sealed 修饰符也可以提高一定的运行效率,因为不用考虑继承
sealed(C# 参考)
当对一个类应用 sealed 修饰符时,此修饰符会阻止其他类从该类继承。
在下面的示例中,类 B 从类 A 继承,但是任何类都不能从类 B 继承。

   class A {}  
   sealed class B : A {}
还可以在重写基类中的虚方法或虚属性的方法或属性上使用 sealed 修饰符。
这将使您能够允许类从您的类继承,并防止它们重写特定的虚方法或虚属性。
在下面的示例中,C 从 B 继承,但 C 无法重写在 A 中声明并在 B 中密封的虚函数 F。

 

Code

 

说明:
当在类中定义新的方法或属性时,通过不将这些方法或属性声明为 virtual,可防止派生类重写这些方法或属性。
将 abstract 修饰符用于密封类是错误的做法,因为抽象类必须由提供抽象方法或属性的实现的类继承。

当应用于方法或属性时,sealed 修饰符必须始终与 override 一起使用。

由于结构是隐式密封的,因此它们不能被继承。

 

 

abstract & virtual
abstract 修饰符可以和类、方法、属性、索引器及事件一起使用。在类声明中使用 abstract 修饰符以指示类只能是其他类的基类。接下来描述了abstract的一些特性:不能实例化;可以包含抽象方法和抽象访问器;不能用 sealed 修饰符修改抽象类,意味着该类不能被继承;从抽象类派生的非抽象类必须包括继承的所有抽象方法和抽象访问器的实现;
在方法或属性声明中使用 abstract 修饰符以指示此方法或属性不包含实现等等。"abstract"可以修饰类、方法、属性、索引器及事件。

而看到virtual关键字的时候,产生了一个疑问:"virtual"关键字只能修饰方法和属性,却不能修饰类~但清楚记得老师讲c++的时候有一个概念,叫虚基类。难道c#把virtual的这个特性去掉了?然后我到google找答案~看到了两篇文章“c++虚基类”和“深入剖析c#继承机制”,终于明白了原来是由于这两者的继承机制不同而决定的。c++允许多重继承,而c#只允许派生类从一个类中继承,由于多重继承产生的二义性问题需要虚基类来解决,所以c++有虚基类的概念,而c#由于单继承的原因不会产生继承而带来的二义性问题,
所以不需要virtual关键修饰类。

链接:
c++虚基类  http://www.yesky.com/496/193996.shtml
深入剖析c#继承机制 http://www.yesky.com/SoftChannel/72342380468109312/20030726/1717126.shtml

 

 

 

checked
  检查是否有内存溢出
int a=200;
byte b=checked(byte(a+100));//b的值是44,抛出异常
2、checked语句
checked
{
   int a=200;
   byte b=checked(byte(a+100));//b的值是44,抛出异常
}
3、注意在checked语句中
checked
{
   int a=200;
   byte b=checked(byte(a+100));//b的值是44,抛出异常
   IncludeOverFlow();//包含溢出,单不会抛出异常
}
  checked 和 unchecked
这两个操作符用于整型算术运算符控制当前的溢出检查
class Test
{
 static  int x=10000;
static int x=10000;
  static inf F(){
return checked(x*y);  //编译错误,抛出异常
}
 static int G(){
   return unchecked(x*y);   //返回值为 -727379968
}

   static int  H(){
   return x*y;      依赖于编译的默认情况
}
}

 


  readonly和const是两种不同的常量,其中readonly是运行时常量,而const是编译时常量。
     举例:
    public static readonly int A = 2; //A为运行时常量
    public const int B = 3; //B为编译时常量
    注意,const默认为static类型,所以无需用static修饰,如果强制用static进行声明将导致编译错误。
    下面的表达式: int C = A + B;
    经过编译后与下面的形式等价:
    int C = A + 3;
    可以看到,其中的const常量B被替换成字面量3,而readonly常量A则保持引用方式。
    const常量只能被赋予数字(整数、浮点数)、字符串以及枚举类型。下面的代码无法通过编译:
  public const DateTime D = DateTime.MinValue;
    改成readonly就可以正常编译:
public readonly DateTime D = DateTime.MinValue;
二、const常数表达式在编译时是完全可以计算的,因此不能通过一个变量的值来进行初始化,比如:
public    const int A=b+1;
  此时的b是一个变量 ,显然,在编译时是不可以计算的。因此,const常数表达式(变量)不可以用变量来初始化。
 而 readonly可以把设定一个字段为常量,但由于它是运行时计算的,因此可以通过变量进行初始化。
三:两者的区别之处:
1. const 字段只能在该字段的声明中初始化。
    对于某个类来说:readonly常量是其实例成员。因此readonly 字段可以在声明或构造函数中初始化。
因此,根据所使用的构造函数,readonly字段可能具有不同的值。
2. const 字段是编译时常数,而 readonly 字段可用于运行时常数。
3. const 默认就是静态的,而 readonly 如果设置成静态的就必须显示声明。
4. const 对于引用类型的常数,可能的值只能是 string 和 null。对于值类型的常数可以是数字(整数、浮点数)以及枚举类型
   readonly可以是任何类型

 

 

 

在迭代器块中用于向枚举数对象提供值或发出迭代结束信号。它的形式为下列之一:
yield return expression;
yield break;
参数
expression
进行计算并以枚举数对象值的形式返回。expression 必须可以隐式转换为迭代器的 yield 类型。
备注
yield 语句只能出现在 iterator 块中,该块可用作方法、运算符或访问器的体。这类方法、运算符或访问器的体受以下约束的控制:
不允许不安全块。
方法、运算符或访问器的参数不能是 ref 或 out。
yield 语句不能出现在匿名方法中。有关更多信息,请参见匿名方法(C# 编程指南)。
当和 expression 一起使用时,yield return 语句不能出现在 catch 块中或含有一个或多个 catch 子句的 try 块中。
示例
在下面的示例中,迭代器块(这里是方法 Power(int number, int power))中使用了 yield 语句。
当调用 Power 方法时,它返回一个包含数字幂的可枚举对象。注意 Power 方法的返回类型是 IEnumerable(一种迭代器接口类型)。
// yield-example.cs
using System;
using System.Collections;
public class List
{
    public static IEnumerable Power(int number, int exponent)
    {
        int counter = 0;
        int result = 1;
        while (counter++ < exponent)
        {
            result = result * number;
            yield return result;
        }
    }

    static void Main()
    {
        // Display powers of 2 up to the exponent 8:
        foreach (int i in Power(2, 8))
        {
            Console.Write("{0} ", i);
        }
    }
}
输出
2 4 8 16 32 64 128 256
在没有yield关键字之前,我们实现Power方法也许要使用下面的方法:
    public static IEnumerable Power(int number, int exponent)
    {
        int counter = 0;
        int result = 1;
        dim al as new arraylist();
        while (counter++ < exponent)
        {
            result = result * number;
            al.add(result);
        }
        return al;
    }

从上面的比较可以看出:yield 只是部分简化了我们的操作。

 

 

 

很多人对Volatile都不太了解,其实Volatile是由于编译器优化所造成的一个Bug而引入的关键字。
         int a = 10;
         int b = a;
         int c = a;
         理论上来讲每次使用a的时候都应该从a的地址来读取变量值,但是这存在一个效率问题,就
是每次使用a都要去内存中取变量值,然后再通过系统总线传到CPU处理,这样开销会很大。所以那些
编译器优化者故作聪明,把a读进CPU的cache里,像上面的代码,假如a在赋值期间没有被改变,就直
接从CPU的cache里取a的副本来进行赋值。但是bug也显而易见,当a在赋给b之后,可能a已经被另一
个线程改变而重新写回了内存,但这个线程并不知道,依旧按照原来的计划从CPU的cache里读a的副
本进来赋值给c,结果不幸发生了。
        于是编译器的开发者为了补救这一bug,提供了一个Volatile让开发人员为他们的过失埋单
,或者说提供给开发人员了一个选择效率的权利。当变量加上了Volatile时,编译器就老老实实的每
次都从内存中读取这个变量值,否则就还按照优化的方案从cache里读。

volatile的本意是一般有两种说法--1.“暂态的”;2.“易变的”。
这两种说法都有可行。但是究竟volatile是什么意思,现举例说明(以Keil-c与a51为例
例子来自Keil FQA),看完例子后你应该明白volatile的意思了,如果还不明白,那只好
再看一遍了。
volatile 关键字指示一个字段可以由多个同时执行的线程修改。
声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制。
这样可以确保该字段在任何时间呈现的都是最新的值。

volatile 修饰符通常用于由多个线程访问但不使用 lock 语句对访问进行序列化的字段。

volatile 关键字可应用于以下类型的字段:

引用类型。

指针类型(在不安全的上下文中)。
请注意,虽然指针本身可以是可变的,但是它指向的对象不能是可变的。
换句话说,您无法声明“指向可变对象的指针”。

整型,如 sbyte、byte、short、ushort、int、uint、char、float 和 bool。

具有整数基类型的枚举类型。

已知为引用类型的泛型类型参数。

IntPtr 和 UIntPtr。

可变关键字仅可应用于类或结构字段。不能将局部变量声明为 volatile。

下面的示例说明如何将公共字段变量声明为 volatile。

// csharp_volatile.cs
// Compile with: /target:library
class Test

 public volatile int i;
   Test(int _i)
  {    
 i = _i;
   }
}

 

operator 关键字用于在类或结构声明中声明运算符。运算符声明可以采用下列四种形式之一:

  1. public static result-type operator unary-operator ( op-type operand )
  2. public static result-type operator binary-operator (
        op-type operand,
        op-type2 operand2
        )
  3. public static implicit operator conv-type-out ( conv-type-in operand )
  4. public static explicit operator conv-type-out ( conv-type-in operand )

参数

  1. result-type 运算符的结果类型。
  2. unary-operator 下列运算符之一:+ - ! ~ ++ — true false
  3. op-type 第一个(或唯一一个)参数的类型。
  4. operand 第一个(或唯一一个)参数的名称。
  5. binary-operator 其中一个:+ - * / % & | ^ << >> == != > < >= <=
  6. op-type2 第二个参数的类型。
  7. operand2 第二个参数的名称。
  8. conv-type-out 类型转换运算符的目标类型。
  9. conv-type-in 类型转换运算符的输入类型。

注意

  1. 前两种形式声明了用户定义的重载内置运算符的运算符。并非所有内置运算符都可以被重载(请参见可重载的运算符)。op-type 和 op-type2 中至少有一个必须是封闭类型(即运算符所属的类型,或理解为自定义的类型)。例如,这将防止重定义整数加法运算符。
  2. 后两种形式声明了转换运算符。conv-type-in 和 conv-type-out 中正好有一个必须是封闭类型(即,转换运算符只能从它的封闭类型转换为其他某个类型,或从其他某个类型转换为它的封闭类型)。
  3. 运算符只能采用值参数,不能采用 refout 参数。
  4. C# 要求成对重载比较运算符。如果重载了==,则也必须重载!=,否则产生编译错误。同时,比较运算符必须返回bool类型的值,这是与其他算术运算符的根本区别。
  5. C# 不允许重载=运算符,但如果重载例如+运算符,编译器会自动使用+运算符的重载来执行+=运算符的操作。
  6. 运算符重载的其实就是函数重载。首先通过指定的运算表达式调用对应的运算符函数,然后再将运算对象转化为运算符函数的实参,接着根据实参的类型来确定需要调用的函数的重载,这个过程是由编译器完成。
  7. 任何运算符声明的前面都可以有一个可选的属性(C# 编程指南)列表。

explicit

explicit 关键字用于声明必须使用强制转换来调用的用户定义的类型转换运算符。

static explicit operator target_type { source_type identifier }

参数

  1. target_type 目标类型
  2. source_type 源类型。
  3. identifier Something。

注意

  1. 转换运算符将源类型转换为目标类型。源类型提供转换运算符。与隐式转换不同,必须通过强制转换的方式来调用显式转换运算符。如果转换操作可能导致异常或丢失信息,则应将其标记为 explicit。这可以防止编译器无提示地调用可能产生无法预见后果的转换操作。

implicit

implicit 关键字用于声明隐式的用户定义类型转换运算符。

static implicit operator target_type { source_type identifier }
注意
  1. 隐式转换可以通过消除不必要的类型转换来提高源代码的可读性。但是,因为可以在程序员未指定的情况下发生隐式转换,因此必须注意防止令人不愉快的后果。一般情况下,隐式转换运算符应当从不引发异常并且从不丢失信息,以便可以在程序员不知晓的情况下安全使用它们。如果转换运算符不能满足那些条件,则应将其标记为 explicit。

示例

以下是一个综合示例,简要展示用法。如要更具体细节的了解,请参阅MSDN Library。


// keywords_operator.cs

using System;

namespace Hunts.Keywords
{
    
// 定义一个人民币结构。数据类型转换的语法对于结构和类是一样的
    public struct RMB
    
{
        
// 注意:这些数的范围可能不能满足实际中的使用
        public uint Yuan;
        
public uint Jiao;
        
public uint Fen;

        
public RMB(uint yuan, uint jiao, uint fen)
        
{
            
if (fen > 9)
            
{
                jiao 
+= fen / 10;
                fen 
= fen % 10;
            }

            
if (jiao > 9)
            
{
                yuan 
+= jiao / 10;
                jiao 
= jiao % 10;
            }

            
this.Yuan = yuan;
            
this.Jiao = jiao;
            
this.Fen = fen;
        }


        
public override string ToString()
        
{
            
return string.Format("¥{0}元{1}角{2}分", Yuan, Jiao, Fen);
        }


        
// 一些操作
        public static RMB operator +(RMB rmb1, RMB rmb2)
        
{
            
return new RMB(rmb1.Yuan + rmb2.Yuan, rmb1.Jiao + rmb2.Jiao, rmb1.Fen + rmb2.Fen);
        }


        
public static implicit operator float(RMB rmb)
        
{
            
return rmb.Yuan + (rmb.Jiao/10.0f+ (rmb.Fen/100.00f);
        }


        
public static explicit operator RMB(float f)
        
{
            
uint yuan = (uint)f;
            
uint jiao = (uint)((f - yuan) * 10);
            
uint fen = (uint)(((f - yuan) * 100% 10);
            
return new RMB(yuan, jiao, fen);
        }


        
// more
    }

    
class App
    
{
        
static void Main()
        
{
            RMB r1, r2, r3, r4;

            
// 记得小学时的某次捐款,我把口袋里藏好的一块钱加6张一毛钱以及13个一分钱的硬币都贡献出去了:(
            r1 = new RMB(1613);
            
// 其实当时其他人都已经交过了,他们总共交了:
            r2 = new RMB(4693);
            
// 那么加上我的就是:
            r3 = r1 + r2;
            Console.WriteLine(
"r3 = {0}", r3.ToString());

            
// 隐式转换
            float f = r3;
            Console.WriteLine(
"float f= {0}", f);

            
// 显式转换
            r4 = (RMB)f;
            Console.WriteLine(
"r4 = {0}", r4.ToString());
            
//如果不进行显示转换,将出现错误 CS0266: 无法将类型“float”隐式转换为“Hunts.Keywords.RMB”。存在一个显式转换(是否缺少强制转换?)

            Console.Read();
        }

    }

}
/*
控制台输出:
r3 = ¥48元6角6分
float f = 48.66
r4 = ¥48元6角5分
*/

我们会发现r4结果少了一分钱!这是因为在:

uint fen = (uint)(((f - yuan) * 100% 10);

这句中,在将float转换为uint时发生了圆整错误(这与计算机以二进制存储有关)。解决这个错误,我们可以使用System.Convert类中用于处理数字的静态方法:

uint fen = Convert.ToUInt32(((f - yuan) * 100% 10);

不过使用System.Convert处理会有些性能的损失。

 

 

extern 修饰符用于声明由程序集外部实现的成员函数
1 基本解释
  extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,
提示编译器遇到此变量和函数时在其他模块中寻找其定义。
  另外,extern也可用来进行链接指定。

经常用于系统API函数的调用(通过 DllImport )。注意,和DllImport一起使用时要加上 static 修饰符

也可以用于对于同一程序集不同版本组件的调用(用 extern 声明别名)

不能与 abstract 修饰符同时使用

示例:


using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Runtime.InteropServices; 
  
namespace Example03 

 
class Program 
 { 
 
//注意DllImport是一个Attribute Property,在System.Runtime.InteropServices命名空间中定义 
 
//extern与DllImport一起使用时必须再加上一个static修饰符 
 [DllImport("User32.dll")] 
 
public static extern int MessageBox(int Handle, string Message, string Caption, int Type); 
  
 
static int Main() 
 { 
 
string myString; 
 Console.Write(
"Enter your message: "); 
 myString 
= Console.ReadLine(); 
 
return MessageBox(0, myString, "My Message Box"0); 
 } 
 } 

 

发现时候已经是中午了,先弄到这里吧,有时间再总结一下……

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多