c#

位置:IT落伍者 >> c# >> 浏览文章

C#基础知识回顾


发布日期:2021年03月20日
 
C#基础知识回顾

今天在一个网站看到了一篇关于C#基础知识的文章篇幅很长浏览了一下确实是基础知识虽然顺序凌乱没有章法但是对于我们经常写代码的人确实应该尽量多的记住一些基础知识 掌握牢固的基础知识编程才能得心应手最基本的东西也应该注重因为细节决定成败

引用类型是类型安全的指针它们的内存是分配在堆(保存指针地址)上的

string数组接口和委托都是引用类型

强制类型转换与as类型转换的区别当类型转换非法时强制类型转换将抛出一个systeminvalidcastexception异常而as不会抛出异常它返回一个null值

用using创建别名

view plainprint?

using console = nsole访问限定符public 该成员可以被其他任何类访问protected 该成员只能被其派生类访问private 该成员只能被本类的其他成员访问internal 该成员只能在当前编译单元的其他成员访问

带参数列表和返回值的main方法

view plainprint?

class test { public static int main(string[] args)

{ foreach (string arg in args)

{……

}构造函数(constructor)包括实例构造函数和静态构造函数

构造函数与类名相同且不能有返回值

view plainprint?

class testclass { testclass() //实例构造函数可以访问静态成员和实例成员用于初始化实例成员{……

}

static testclass() //静态构造函数只能访问静态成员用于初始化静态成员{……

}类的静态成员属于类所有不必生成实例就可以访问它是在载入包含类的应用程序时创建的但静态方法不能访问类的实例变量和方法通常静态变量是在定义时就赋初始值的

类的实例成员属于类的实例所有不创建实例对象就无法对其进行访问实例成员可以访问类的静态成员和其它实例成员

调用基类的析构函数

view plainprint?

class a { public a()

{……

}

class b { public b() base() //调用基类的析构函数{……

}常量其值是在编译时设定的必须是数值文字默认状态下常量是静态的

view plainprint?

class a { public const double pi = }常量是编译时就确定的值只读字段是在运行才能确定的值比如运行时才能确定的屏幕分辨率

只读字段只能在类的析构函数中赋值

静态只读字段

view plainprint?

class a { public static readonly int screenwidth //静态只读字段static a() //静态析构函数{ screenwidth = //在静态析构函数中初始化}在类的继承中类的析构函数是不会被继承的

一个派生类只能从一个基类继承不能同时从多个基类继承但可以通过继承多个接口来达到相同目的实现多继承的唯一方法就是使用接口

view plainprint?

class myfancygrid control iserializable idatabound {……

}密封类是不能继承的类抽象类不能被定义为密封类且密封类的私有成员不能用protected修饰只能用private

view plainprint?

sealed class a {……

}关键字ref和out用于指定用引用方式传递方法的参数

它们的区别是ref参数必须初始化而out参数不需要初始化所以在方法处理代码依赖参数的初始化值时使用ref不依赖初始化值时使用out对out参数即使在传递前对其进行了初始化其值也不会传递到方法处理函数内部传递时系统会将其设为未初始化所以在方法内部必须对out参数进行初始化

方法重载时必须参数数目和参数类型其中之一不同返回值不同不能作为重载

c#不支持方法的默认值只能通过方法重载来实现

view plainprint?

class a { int method(int a)

{……

}

void method(int a int b) //参数数目不同{ //返回值不同不能作为重载……

} view plainprint?

params参数用于一个不定数目参数的方法一般后面跟一个数组class a { public void method(params int[] i)

{……

}方法的覆盖指派生类覆盖基类的同名方法有二种方法)第一种是在派生类要覆盖的方法前面加new修饰而基类不需要作任何改动

这种方法的缺点是不能实现多态

view plainprint?

class a { public void method() //无需任何修饰{……

}

class b a //从基类继承{ new public void method() //覆盖基类的同名方法{……

}

class testclass { a instance = new b()thod() //这时将调用类a的method方法而不是类b的method方法} )第二种是在派生类要覆盖的方法前面加override修饰而基类的同名方法前面加virtual修饰

这样就能实现多态

view plainprint?

class a { virtual public void method() //基类定义虚方法{ //虚拟方法不能定义为private因为private成员对派生类是无法访问的……

}

class b a //从基类继承{ override public void method() //派生类覆盖基类的同名虚方法{……

}

class testclass { protected void test()

{ a instance = new b() //定义一个实例类型为基类从派生类创建//派生类总是能够向上转换为其基类thod() //将调用派生类b的method方法而不是基类的这就是多态}说明new修饰的方法覆盖不能实现多态的原因是因为使用new时编译器只会实现早期绑定(early binding)

即调用的方法在编译时就决定了编译器看到thod()而instance的类是a就会调用类a的method()方法

override修饰的方法覆盖可以实现多态的原因是因为实现了后期绑定(late binding)

使用override时强制编译器在运行时根据类的真正类型正确调用相应的方法而不是在编译时

而基类的同名方法必须加virtual修饰

类的静态方法可能通过 类名静态方法名 这种格式来调用不能使用 实例名静态方法名 这种方法调用

因为类的静态方法为类所有(是属于类本身的)而非实例所有(不是属于类的实例的)

类的静态方法可以访问类的任何静态成员但不能访问类的实例成员

c#中类的变量称为字段类的public变量称为类的公共字段

类的属性由一个protected(也可以是private)字段和getter和setter方法构成

view plainprint?

class address { protected string zipcode //protected字段注意大小写public string zipcode { get //getter方法{ return zipcode} set //setter方法{ zipcode = value //被传递的值自动被在这个value变量中} }}只读属性是指省略setter方法的属性只读属性只能读取不能设置

属性也可以用限定符virtualoverride和abstract修饰功能同其他类的方法

属性有一个用处称为懒惰的初始化(lazy initialization)即在需要类成员时才对它们进行初始化如果类中包含了很少被引用的成员而这些成员的初始化又会花费大量的时候和系统资源的话懒惰的初始化就很有用了

c#中数组对象共同的基类是systemarray将数组声明为类的一个成员时声明数组与实例化数组必须分开这是因为只能在运行时创建了类的实例对象之后才能实例化数组元素值

声明

view plainprint?

int[] intarray //一维数组int[] intarray //三维数组初始化

view plainprint?

intarray = new int[] {}int[] intarray = new int[] {{}{}} //声明时可以初始化遍历)一维数组

view plainprint?

for (int i = i < intarraylength i++) //arraylength返回数组所有元素的个数foreach (int i in intarray)for (int i = i < intarraygetlength( i++)//arraygetlength()返回数组第一维的个数)多维数组

view plainprint?

for (int i = i < intarraygetlength( i++) //遍历三维数组for (int j = j < intarraygetlength( j++)

for (int k = k < intarraygetlength( k++)

{……

}数组的维数就是该数组的秩(rank)arrayrank可以返回数据的秩

锯齿数组(jagged array)是元素为数组的数组

view plainprint?

int[][] jaggedarray = new int[][] //包含二个元素每个元素是个数组jaggedarray[] = new int[] //每个元素必须初始化jaggedarray[] = new int[]for (int i = i < jaggedarraylength i++) //遍历锯齿数组for (int j = j < jaggedarray[i]length j++)

{……

}类的属性称为智能字段类的索引器称为智能数组由于类本身作数组使用所以用this作索引器的名称索引器有索引参数值

view plainprint?

using systemusing llections

class mylistbox { protected arraylist data = new arraylist()public object this[int idx] //this作索引器名称idx是索引参数{ get { if (idx > && idx < unt)

{ return data[idx]} else { return null} set { if (idx > && idx < unt)

{ data[idx] = value} else if (idx = unt)

{ dataadd(value)} else { //抛出一个异常}接口是二段不同代码之间约定通过约定实现彼此之间的相互访问

c#并不支持多继承但通过接口可实现相同功能

当在接口中指定了实现这个接口的类时我们就称这个类实现了该接口从接口继承

一个接口基本上就是一个抽象类这个抽象类中除了声明c#类的其他成员类型??例如属性事件和索引器之外只声明了纯虚拟方法

接口中可以包含方法属性索引器和事件??其中任何一种都不是在接口自身中来实现的

view plainprint?

interface iexampleinterface { //property declaration int testproperty { get }

//event declaration event testevevnt changed

//mothed declaration function void testmothed()

//indexer declaration string this[int index] { get set } }说明定义接口时在方法属性事件和索引器所有这些接口成员都不能用public之类的访问限定符因为所有接口成员都是public类型的

因为接口定义了一个约定任何实现一个接口的类都必须定义那个接口中每一个成员否则将编译失败

view plainprint?

using systempublic class fancycontrol { protected string datapublic string data { get {return thisdata} set {data = value} }

interface ivalidate { bool validate() //接口方法}

public class mycontrol fancycontrol ivalidate { public mycontrol()

{ data = my control data}

public bool validate() //实现接口{ if (data == my control data

return trueelse return false}

class interfaceapp { mycontrol mycontrol = new mycontrol()

ivalidate val = (ivalidate)mycontrol //可以将一个实现某接口的类转换成该接口bool success = valvalidate() //然后可调用该接口的方法}也可以用

view plainprint?

bool success = mycontrolvalidate()这种方法来调用validate方法因为validate在类mycontrol中是被定义成public的如果去除publicvalidate方法被隐藏就不能用这种方法调用了这样隐藏接口方法称为名字隐藏(name hiding)

可以用类实例 is 接口名 来判断某个类是否实现了某接口mycontrol is ivalidate //mycontrol类的实例mycontrol是否实现了ivalidate接口当然也可用as来作转换根据转换结果是否为null来判断某个类是否实现了某接口

view plainprint?

ivalidate val = mycontrol as ivalidateif (null == val)

{…… //没有实现ivalidate接口} else {…… //实现了ivalidate接口}如果一个类从多个接口继承而这些接口中如果定义的同名的方法则实现接口的方法时必须加接口名来区别写成 接口名方法名假设test类从idatastore和iserializable二个接口继承而这二个接口都有savedata()方法实现savedata()方法时必须写成

view plainprint?

class test iserializable idatastore { void iserializablesavedata()

{……

}

void idatastoresavedata()

{……

}如果一个类从多个接口继承为了方便可以定义一个新的接口这个接口继续多个接口然后类直接从这个接口继承就可以了这个叫合并接口

view plainprint?

interface isavedata iserializable idatastore { //不需要定义任何方法或成员只是用作合并} class test isavedata //只要继承isavedata就可以了{……

} c# 操作符优先级(从高到低)

初级操作符 () xy f(x) a[x] x++ x—— new typeof sizeof checked unchecked一元操作符 + | ~ ++x ——x (t)x乘除操作符 * / %加减操作符 + 位移操作符 << >>关系操作符 < > <= >= is等于操作符 ==逻辑与 &逻辑异或 ^逻辑或 |条件与 &&条件或 ||条件操作符 ?赋值操作符 = *= /= %= += = <<= >>= &= ^= |=

所有的二元操作符除赋值符外都是左联合的即从左到右计算

typeof()运算符可以从一个类名得到一个systemtype对象而从systemobject对象继承来的gettype()方法则可从一个类实例来得到一个systemtype对象

view plainprint?

type t = typeof(apple) //apple是一个类名apple apple = new apple() //apple是apple类的一个实例type t = applegettype() //t与t是相同的通过反射得到一个类的所有成员和方法

view plainprint?

type t = typeof(apple)string classname = ttostring() //得到类名methodinfo[] methods = tgetmethods() //得到所有方法foreach (methodinfo method in methods)

{ //用methodtostring()得到方法名} memberinfo[] members = tgetmembers() //得到所有成员foreach (memberinfo member in members)

{ //用membertostring()得到成员名} sizeof()操作符用来计算值类型变量在内存中占用的字节数(bytes)并且它只能在unsafe(非

安全)

代码中使用

view plainprint?

static unsafe public void showsizes()

{ int i jj = sizeof(short)j = sizeof(i)}尽可能使用复合赋值操作符它比不用复合赋值操作符的效率高

for语句的语法为

view plainprint?

for (initialization booleanexpression step)

embeddedstatement在initialization和step部份还可以使用逗号操作符

view plainprint?

for (int i = j = i <= \xff i++ j++)

for (int i = j = i < i += j j = i j) //输出斐波那契数列consolewrite({} i)在switch语句中执行一个分支的代码后还想执行另一个分支的代码可以用goto case 分支

操作符重载是为了让程序更加自然容易理解想要为一个类重新定义一个操作符使用以下语法public static 返回值 operator 操作符 (操作对象[操作对象])

说明)所有重载的操作符方法都必须定义为public和static )从技术上说返回值可以是任何类型但通常是返回所定义方法使用的类型)操作对象的数目取决于重载是一元操作符还是二元操作符一元操作符只要一个操作对象二元操作符则需要二个

)不管重载是一元操作符还是二元操作符第一个操作对象的类型都必须与返回值的类型一致而对于二元操作符的第二个操作对象的类型则可以是任何类型

)只有下列操作符可以被重载一元+ ! ~ ++ —— true false二元+ * / % & | ^ << >> == != > < >= <=赋值操作符(+==*/=%=等等)无法被重载

[]和()操作符也无法被重载

)操作符的优先级是无法改变的运算优先级的规则是静态的

假设一个invoice发票类由多个invoicedetailline类(成员只有一个double类型的amount金额属性)组成我们重载+操作符使之可以将invoicedetailline类的内容(注意不是金额合计)加在一起

view plainprint?

class invoice { public arraylist detailline

public invoice //类的析构函数{ detailline = new arraylist() //arraylist存放多个invoicedetailline类的实例}

public static invoice operator+ (invoice invoice invoice invoice) //参数与返回值的类型一致{ //invoice与invoice的内容合并invoice returninvoice = new invoice()foreach(invoicedetailline detailline in invoicedetaillines)

returninvoicedetaillineadd(detailline)foreach(invoicedetailline detailline in invoicedetaillines)

returninvoicedetaillineadd(detailline)return returninvoice}

class invoiceaddapp //调用示例{ public static void main()

{ invoice i = new invoice()for(int i = i < i++)

idetaillineadd(new invoicedetailline(i + ))

invoice i = new invoice()for(int i = i < i++)

idetaillineadd(new invoicedetailline(i + ))

invoice summaryinvoice = i + i //调用重载的操作符+方法}自定义类型转换可以编写代码实际二个不同的类结构体之间的转换

语法public static implicite/explicite operator 输出类型 (输入类型)

说明)转换方法必须是静态的

)implicite表示隐式转换explicite表示显式转换

)输入类型和输出类型其中之一必须与包含转换的类或结构体类型即转换必须与本类相关

view plainprint?

struct celisus { public float t

public celisus(float t)

{ thist = t //thist是结构体的字段t是参数}

public static implicite operator celisus(float t) //float=>celisus { return new celisus(t)}

public static implicite operator float(celisus c) //celisus=>float { return ((ct ) / Array) * }代表的(delegate)目的与c++中的函数指针相同代表不是在编译时被定义的而是在运行时被定义的

代表主要有二个用途回调(callback)和事件处理(event)

回调通常用于异步处理和自定义处理

view plainprint?

class dbmanager { static dbconnection[] activeconnections//声明回调函数public void delegate enumconnectioncallback(dbconnection connection)

public static void enumconnections(enumconnectioncallback callback)

{ foreach (dbconnection connection in activeconnections)

{ callback(connection) //执行回调函数} //调用

view plainprint?

class delegateapp { public static void activeconncetioncallback(dbconnection connection) //处理函数{……

}

public void main()

{ //创建指向具体处理函数的代表实例(新建一个代表让它指向具体的处理函数)

dbmanageremnuconnectioncallback mycallback = new dbmanageremnuconnectioncallback(activeconncetioncallback)dbmanagerenumconnections(mycallback)} //使用静态代表上面的调用改为

view plainprint?

class delegateapp { //创建一个指向处理函数的静态代表public static dbmanageremnuconnectioncallback mycallback = new dbmanageremnuconnectioncallback(activeconncetioncallback)public static void activeconncetioncallback(dbconnection connection)

{……

}

public void main()

{ dbmanagerenumconnections(mycallback)} //在需要时才创建代表上面的调用改为

view plainprint?

class delegateapp { //将创建代表放在属性的getter方法中public static dbmanageremnuconnectioncallback mycallback { get { retun new dbmanageremnuconnectioncallback(activeconncetioncallback)} public static void activeconncetioncallback(dbconnection connection)

{……

}

public void main()

{ delegateapp app = new delegateapp() //创建应用程序dbmanagerenumconnections(mycallback)}可以将多个代表整合成单个代表

view plainprint?

class compositedelegateapp { public static void logevent(part part)

{……

}

public static void emailpurchasingmgr(part part)

{……

}

public static void main()

{ //定义二个代表inventorymanageroutofstockexceptionmethod logeventcallback = new inventorymanageroutofstockexceptionmethod(logevent)inventorymanageroutofstockexceptionmethod emailpurchasingmgrcallback = new inventorymanageroutofstockexceptionmethod(emailpurchasingmgr)//整合为一个代表注意后加的代表先执行(这里是先执行logeventcallback)

inventorymanageroutofstockexceptionmethod onhandexceptioneventscallback = emailpurchasingmgrcallback + logeventcallback//调用代表inventorymanager mgr = new inventorymanager()mgrprocessinventory(onhandexceptioneventscallback)//inventorymanager类的processinventory方法的原型为//public void processinventory(outofstockexceptionmethod exception)}可以根据需要将多个代表自由地组合成单个代表

view plainprint?

class compositedelegateapp { //代表指向的处理函数(三个代表三个函数)

public static void logevent(part part)

{……

}

public static void emailpurchasingmgr(part part)

{……

}

public static void emailstoremgr(part part)

{……

}

public static void main()

{ //通过数组定义三个代表inventorymanageroutofstockexceptionmethod[] exceptionmethods = new inventorymanageroutofstockexceptionmethod[]exceptionmethods[] = new inventorymanageroutofstockexceptionmethod(logevent)exceptionmethods[] = new inventorymanageroutofstockexceptionmethod(emailpurchasingmgr)exceptionmethods[] = new inventorymanageroutofstockexceptionmethod(emailstoremgr)

int location = //再定义一个代表(用于组合成单代表)

inventorymanageroutofstockexceptionmethod compositedelegate//根据需要组合if (location =

{ compositedelegate = exceptionmethods[] + exceptionmethods[]} else { compositedelegate = exceptionmethods[] + exceptionmethods[]} //调用代表inventorymanager mgr = new inventorymanager()mgrprocessinventory(compositedelegate)} c#的事件遵循发布??预订的设计模式在这种模式中一个类公布能够出现的所有事件然后任何的类都可以预订这些事件一旦事件产生运行环境就负责通知每个订户事件已经发生了

当代表作为事件的处理结果时(或者说定义具有代表的事件)定义的代表必须指向二个参数的方法一个参数是引发事件的对象(发布者)另一个是事件信息对象(这个对象必须从eventargs类中派生)

view plainprint?

using system

class inventorychangeeventargs eventargs //事件信息对象从eventargs类派生{…… //假设定义二个public属性string sku和int change }

class inventorymanager //事件的发布者{ //声明代表public delegate void inventorychangeeventhander(object source inventorychangeeventargs e)//发布事件event关键字可将一个代表指向多个处理函数public event inventorychangeeventhandler oninventorychangehander

public void updateinventory(string sku int change)

{ if (change ==

returninventorychangeeventargs e = new inventorychangeeventargs(sku change)//触发事件if (oninventorychangehandler != null) //如果有预订者就触发oninventorychangehandler(this e) //执行代表指向的处理函数}

class inventorywatcher //事件的预订者{ public inventorywatcher(inventorymanager mgr) //mgr参数用于联结发布者{ thisinventorymanager = mgr//预订事件用 += 调用多个处理函数mgroninventroychangehandler += new inventorymanagerinventorychangeeventhandler(oninventorychange)//事件处理函数void oninventroychange(object source inventroychangeeventargs e)

{……

}

inventorymanager inventorymanager}

class eventsapp //主程序{ public static void main()

{ inventorymanager inventorymanager = new inventorymanager()inventorywatcher inventorywatcher = new inventorywatcher(inventorymanager)

inventorymanagerupdateinventory( inventorymanagerupdateinventory( } microsoft windows nt和ibm os/等操作系统都支持占先型多任务在占先型多任务执行中处理器负责给每个线程分配一定量的运行时间??一个时间片(timeslice)处理器接着在不同的线程之间进行切换执行相应的处理在单处理器的计算机上并不能真正实现多个线程的同时运行除非运行在多个处理器的计算机上操作系统调度的多线程只是根据分配给每个线程时间片进行切换执行感觉上就像同时执行

上下文切换(context switching)是线程运行的一部分处理器使用一个硬件时间来判断一个指定线程的时间片何时结束当这个硬件计时器给出中断信号时处理器把当前运行的线程所用的所有寄存器(registers)数据存储到堆栈中然后处理器把堆栈里那些相同的寄存器信息存放到一种被称为上下文结构的数据结构中

当处理器要切换回原来执行的线程时它反向执行这个过程利用与该线程相关的上下文结构在寄存器里重新恢复与这一线程相关的信息这样的一个完整过程称为上下文切换

多线程允许应用程序把任务分割为多个线程它们彼此之间可以独立地工作最大限度地利用了处理器时间

view plainprint?

using systemusing systemthreading

class simplethreadapp { public static void workerthreadmethod() //线程的执行体{…… //执行一些操作}

public static void main()

{ //创建一个线程代表指向线程的执行体threadstart是创建新线程必须用到的代表threadstart worker = new threadstart(workerthreadmethod)thread t = new thread(worker) //用线程代表创建线程tstart() //执行线程}可以通过两种方式来得到一个thread对象一种是通过创建一个新线程来得到如上例另一种在正在执行的线程调用静态的threadcurrentthread方法

静态方法threadsleep(int ms)可以让当前线程(它自动调用threadcurrentthread)暂停指定毫秒的时间

如果使用threadsleep()那么当前线程将一直处于等待中直到另一个线程调用这个线程的实例方法threadinterrupt方法等待才会结束

使用threadsuspend方法也能挂起线程threadsuspend方法可以被当前线程或其他线程调用而threadsleep(

只能由当前线程在执行体中调用当线程用threadsuspend挂起时必须用threadresume方法恢复不论threadsuspend方法调用了多少次只要调用threadresume方法一次就可以线程恢复执行用threadsuspend方法并不会阻塞线程调用立即返回而threadsleep()则会阻塞线程所以确切地说threadsleep()暂停线程而不是挂起线程

>使用threadabort方法可以终止正在执行的线程当threadabort方法被调用时线程不会立即终止执行运行环境将会等待直到线程到达文档中所描述的安全点如果要确保线程已经完全停止可以使用threadjoin方法这是一个同步调用同步调用意味着直到线程完全停止调用才会返回

threadpriority属性用于设置的线程的优先级其值是threadthreadpriority枚举值可以设为highest abovenormalnormal belownormal lowest缺省值是threadthreadprioritynormal

线程的同步是为了解决多个线程同时使用同一对象产生的一些问题通过同步可以指定代码的临界区(critical section)一次只有一个线程可以进入临界区

使用systemmonitor类(锁定与信号量)进行线程同步

view plainprint?

using systemusing systemthreading

public void savedata(string text) //线程执行函数或线程执行函数调用的对象的方法{…… //执行其他一些不需要同步的处理

monitorenter(this) //获取对象的monitor锁…… //执行需要同步的处理monitorexit(this) //释放对象的monitor锁

…… //执行其他一些不需要同步的处理}说明当执行monitorenter方法时这个方法会试图获取对象上的monitor锁如果另一个线程已经拥有了这个锁这个方法将会阻塞(block)直到这个锁被释放

也可用c#的lock语句来获得和释放一个monitor锁上面同步写成

view plainprint?

public void savedata(string text) //线程执行函数或线程执行函数调用的对象的方法{…… //执行其他一些不需要同步的处理

lock(this) //获取对象的monitor锁代码块执行完成后释放monitor锁{…… //执行需要同步的处理}

…… //执行其他一些不需要同步的处理}也可以使用systemthreading名称空间的mutex类(互斥类)进行线程同步与monitor锁一样一次只有一个线程能获得一个给定的互斥但mutex要慢得多但它增加了灵活性

view plainprint?

using systemusing systemthreading

class database { mutex mutex = new mutex(false) //创建一个互斥但不立即获得它//注意创建互斥在需要同步的方法之外实际上它只要创建一个实例public void savedata(string text) //需要同步的方法{ mutexwaitone() //等待获得互斥…… //需要同步的处理mntexclose() //释放互斥} mutex类重载了三个构造函数mutex() //创建并使创建类立即获得互斥mutex(bool initiallyowned) //创建时可指定是否要立即获得互斥mutex(bool initiallyowned string mutername) //还可以指定互斥的名称

mutexwaitone方法也重载了三次mutexwaitone() //一直等待mutexwaitone(timespan time bool exitcontext) //等待timespan指定的时间mutexwaitone(int milliseconds bool exitcontext) //等待指定的毫秒

线程的用法)并发操作比如一个程序监视多个com口当每个com接到信息时执行一段处理时

)复杂长时间操作一个长时间的复杂操作可能会使界面停滞停止用户响应如果还允许用户停止它或者显示进度条显示操作执行进程信息时

反射(reflection)就是能够在运行时查找类型信息这是因为net编译的可执行(pe)文件中包括msil和元数据(metadata)

反射的中心是类systemtypesystemtype是一个抽象类代表公用类型系统(common type system cts)中的一种类型

view plainprint?

using systemusing systemreflection //反射命名空间必须引用

public static void main(string[] args)

{ int i = type t = igettype() //根据实例得到类型t = typegettype(systemint //根据类型的字符名称得到类型}通过assembly类可以得到已经编译net framework程序的中所有类型

view plainprint?

using systemusing systemdiagnostics //为了使用process类using systemreflection //为了使用assembly类

class gettypesapp { protected static string getassemblyname(string[] args)

{ string assemblynameif ( == argslength) //如果参数为空取当前进程的名称{ process p = processgetcurrentprocess()assemblyname = pprocessname + exe} else assemblyname = args[] //取第一个参数即当前运行程序名

return assemblyname}

public static void main(string[] args)

{ string assemblyname = getassemblyname(args)assembly a = assemblyloadfrom(assemblyname) //调用编译程序集type[] types = agettypes() //得到多个类型foreach (type t in types) //遍历类型数组{…… //取得tfullnametbasetypefullname等类型信息}一个应用程序可以包括多个代码模块若要将一个cs文件编译一个模块只要执行下面的命令csc /targetmodule 要编译的模块cs //csc是c sharp compiler(c#编译器)

然后在应用程序中using编译的模块cs中的namespace即可应用了

要反射应用程序中所有代码模块(module)只要view plainprint?

assembly a = assemblyloadfrom(assemblyname) //应用程序的物理文件名module[] modules = agetmodules()foreach(module m in modules)

{…… //显示mname等}后期绑定(latebinding)

view plainprint?

string[] filenames = directorygetfiles(environmentcurrentdirectory *dllforeach (string filename in filenames)

{ assembly a = assemblyloadfrom(filename)type[] types = agettypes()foreach(type t in types)

{ if (tissubclassof(typeof(commprotocol))) //判断是否有commprotocol的派生类{ object o = activatorcreateinstance(t) //生成实例methodinfo mi = tgetmethod(displaynamemiinvoke(o null) //调用方法} //带参数的例子

view plainprint?

namespace programming_csharp { using systemusing systemreflection

public class tester { public static void main( )

{ type t = typegettype(systemmathobject o = activatorcreateinstance(t)

// 定义参数类型type[] paramtypes = new type[]paramtypes[]= typegettype(systemdouble

methodinfo cosineinfo = tgetmethod(cos paramtypes)

//设置参数数据object[] parameters = new object[]parameters[] =

//执行方法object returnval = cosineinfoinvoke(o parameters)consolewriteline(the cosine of a degree angle {} returnval)}动态生成代码和动态调用的完整例子//动态生成代码的部分view plainprint?

using systemusing systemreflectionusing systemreflectionemit //动态生成代码必须引用

namespace ilgenserver { public class codegenerator { public codegenerator()

{ currentdomain = appdomaincurrentdomain //得到当前域assemblyname = new assemblyname() //从域创建一个程序集assemblynamename = tempassembly//得到一个动态编译生成器assemblybuileraccessrun表示只在内存中运行不能保存assemblybuilder = currentdomaindefinedynamicassembly(assemblyname assemblybuileraccessrun)//从编译生成器得到一个模块生成器modulebuilder = assemblybuilderdefinedynamicmodule(tempmodule//模块生成器得到类生成器typebuilder = modulebuilderdefinetype(tempclass typeattributespublic)//为类添加一个方法methodbuilder = typebuilderdefinemethod(helloword methodattributespublic null null)//为方法写入代码生成代码必须使用到il生成器msil = methodbuildergetilgenerator()msilemitwriteline(hello worldmsilemit(opcodesret)//最后还需要编译(build)一下类t = typebuildercreatetype()}

appdomain currentdomainassemblyname assemblynameassemblybuilder assemblybuildermodulebuilder modulebuildertypebuilder typebuildermethodbuilder methodbuilderilgenerator msilobject otype tpublic type t { get { return thist} //动态调用的部分

view plainprint?

using systemusing systemreflectionusing ilgenserver //引用动态生成代码的类

public class ilgenclientapp { public static void main({ codegenerator gen = new codegenerator() //创建动态生成类type t = gentif (null != t)

{ object o = activatorcreateinstance(t)methodinfo helloworld = tgetmethod(helloworld //为调用方法创建一个methodinfo if (null != helloworld)

{ helloworldinvoke(o null) //调用方法}调用dll

view plainprint?

using systemusing systemruntimeinteropservices //为了使用dllimport特性

class pinvokeapp { [dllimport(userdll charset=charsetansi)] //charsetansi指定ansi版本的函数(messageboxa)charsetunicode指定unicode版本的函数(messageboxw)

static extern int messagebox(int hwnd string msg string caption int type) //声明dll中的函数

//[dllimport(userdll entrypoint=messageboxa)] //用这种方法使用不同的函数名//static extern int msgbox(int hwnd string msg string caption int type)

//[dllimport(userdll charset=charsetunicode)] //调用unicode版的dll函数//static extern int messagebox(int hwnd [marshalas(unmanagedtypelpwstr)]string msg// [marshalas(unmanagedtypelpwstr)]string caption int type) //将lpwstr翻译为string型缺省情况系统只将lpstr翻译成string public static void main()

{ messagebox( hello world! captionstring //调用dll中的函数}例使用回调

view plainprint?

class callbackapp { [dllimport(userdll)] static extern int getwindowtext(int hwnd stringbuilder text int count)

delegate bool callbackdef(int hwnd int lparam)

[dllimport(userdll)] static extern int enumwindows(callbackdef callback int lparam)

static bool printwindow(int hwnd int lparam)

{ stringbuilder text = new stringbuilder(getwindowtext(hwnd text consolewriteline(window caption {} text)return true}

static void main()

{ callbackdef callback = new callbackdef(printwindow)enumwindows(callback }关键字unsafe指定标记块在非控环境中运行该关键字可以用于所有的方法包括构造函数和属性甚至还有方法中的代码块关键字fixed负责受控对象的固定(pinning)pinning是一种动作向垃圾收集器(garbage collector gc)指定一些不能被移动的对象为了不在内存中产生碎片net运行环境把对象四处移动以便于最有效地利用内存使用fixed后指定对象将不会被移动所以就可以用指针来访问它

c#中只能得到值类型数组和字符串的指针在数组的情况下第一个元素必须是值类型因为c#实际上是返回一个指向数组第一个元素的指针而不是返回数组自身

& 取一个变量的内存地址(即指向该变量的指针)

* 取指针所指变量的值> 取成员例

view plainprint?

using system

class unsafeapp { public static unsafe void getvalues(int* x int* y)

{ *x = *y = }

public static unsafe void main()

{ int a = int b = getvalues(&a &b)} fixed语法为fixed(type* ptr = expression) statements其中type也可以为非控类型也可是voidexpression是任何产生一个type指针的表达式statements是应用的代码块

view plainprint?

fixed (int* f = &foox) //foo是foo类的一个实例x是foo类的一个int属性{ setfoovalue(f) //setfoovalue方法的定义为unsafe static void setfoovalue(int* x)

}传统的com组件可以通过互操作层(com interop)与net运行环境交互互操作层处理在托管运行环境和非托管区域中的com组件操作之间传递所有的消息

要使com组件能在net环境中使用必须为com组件生成元数据net运行环境用元数据层业判断类型信息在运行时刻使用类型信息以便生成rcw(runtime callable wrapper运行时可调用包装)net应用程序与com对象交互时rcw处理对com对象的装载和调用rcw还完成许多其他的工作如管理对象标识对象生存周期以及接口缓沖区

对象生存周期管理十分关键因为net gc把对象到处移动并且当对象不再使用时自动处理这些对象rcw服务告诉net应用程序正与托管net组件交互同时又使非托管com组件觉得com对象是被传统的com客户端调用的

为了为com组件生成元数据包装必须使用tlbimpexe(typelib importer)工具tlbimp some_comtlb /outsom_comdll

               

上一篇:.NET 分部方法

下一篇:运行未安装VS2005的机器上C++程序