您的位置:首页 > 编程语言 > C#

读书笔记_C#技术内幕_第三十章(不安全代码和平台调用)

2008-09-07 12:41 267 查看
在任何项目中的一个重要的考虑因素是再利用现有的代码。访问包含在业务逻辑或低级系统功能中的遗留代码(legacy code)能够在时间和成本方面都产生重大的利益。为满足这个要求,C#提供了用PInvoke功能来访问遗留系统的机制。PInvoke是平台调用(platform invoke)的缩写。
不安全代码:
不安全代码可以使用指针,它支持某些性能优化和遗留代码及操作系统的接口。不安全代码是用特殊的关键字unsafe来标识的,它可以标识代码块或字段。这就建立了unsafe(不安全)的内容,其中能够实现指针操作。
有与不安全的内容相关联的特殊的关键字,使它容易使用指针。其中fixed关键字帮助约束存储器中的对象,因而在操作中,垃圾收集器不能够移动它们。通过使用sizeof操作符可以获得指针或字段的大小。stackalloc操作符使得存储器可以在堆栈上分配。除了关键字之外,还有几个便于指针操作的其它操作符,例如,间接引用(dereference)操作符(*)、操作符地址(&)和间接操作符(->)。

代码不安全是什么含义
这是一个相当容易混淆和需要讨论的主题,C#带来了与代码是安全(safe)、不安全(unsafe)、托管(managed)和非托管(unmanaged)有关的新词汇。
安全——在C#程序中操作的普通模式是安全的。当代码是安全的时,它就是安全类型并且安全可靠。虽然没有叫做安全的正式代码类型,但它表明了它与不安全代码的区别。
不安全——不安全代码时通过unsafe关键字标识的。这是可以使用指针的代码。它也只放在某些语句和操作符中,例如,可以使用fixed、sizeof和stackalloc等。不安全代码比普通的安全代码更复杂,并且更容易出错误,因此需要unsafe关键字把它与普通的安全代码分隔开。
虽然不安全代码可以进行普通C#程序中不允许的某些操作,但是不安全代码仍然是托管。它之所以是托管,是因为通用运行时(Common Language Runtime,CLR)和虚拟执行系统(Virtual Execlltion System,VES)仍然对代码有控制,并且仍然管理着存储器。
托管——所有的C#代码都是托管。托管代码是在CLR的控制之下,它对所有存储器和安全性操作有完全的控制。不安全代码仍然是可管理代码。
托管类型全部都是引用类型和具有嵌套引用类型的值类型。托管类型驻留在堆(heap)上,并且由CLR管理。
非托管——本机代码,或叫做原码,例如,通过平台调用或COM Interop访问的代码是非托管,非托管代码不受CLR的控制。
非托管类型包括全部值类型(没有嵌套的引用类型)、枚举和指针。

指针的功能:
指针是到另一个对象的间接地址。在它的最基本的级上,它类似于对象的引用,但是其功能强大的多。当引用提供了引用对象的机制时,指针能够被算术的操纵来向前和向后移动通过对象组。指针能够设置为任何可寻址的位置,甚至可查看没有对象存在的存储器的位置。
从分类的观点来看,指针类型是值类型和引用类型的对等体。指针声明为特定的值或引用类型。这就意味着它们包含有它们所声明的类型的地址。如:
int *intPtr;

int* intPtr;

int * intPtr;
把一个字段的地址赋值给指针的方法如下:
int myInt = 7;
intPtr = &myInt;
下面的例子展示如何使用间接操作符来使指针能够从普通字段读取值:
int retrievedInt = *intPtr;
指针可以有多个级别的间接寻址,它是指向指针的指针。下面是声明指向指针的指针的例子。
int **intPtrPtr = &intPtr;
intPtr的地址(即指针本身)赋值给另一个指针intPtrPtr。这时,需要两个*符号来声明intPtrPtr,因为它是到类型为int指向指针的指针。

SizeOf操作符
用于知道类型的大小。

Stackalloc操作符
用于在堆栈上分配存储器。
class StackAllocDemo
{
unsafe static void Main(string[] args)
{
string myString = "Unsafe is still Managed!";

char *charArr = stackalloc char[myString.Length];
char* charPtr = charArr;

int count = 0;
foreach (char c in myString)
{
*charPtr++ = c;
Console.Write("{0}", charArr[count++]);
}
}
}
注意:stackalloc操作符分配堆栈上的存储器。如果需要分配堆上的存储器,就应该创建一个类。该类使用PInvoke来调用操作系统存储器分配例程。例如:Windows的HeapAlloc()和HeapFree()函数就能够分配和释放堆上的存储器。

fixed语句
fixed语句在用指针访问可移动的对象时,保持该对象不动。当使用fixed语句时,固定一个变量,然后就认为它是固定的。因为垃圾收集器和其他存储器优化进程的影响,不能够保证在一个操作中所指的对象就是下一次指针引用的同一个对象。fixed语句可以保证可移动的对象保持不动。
当使用fixed语句时有两种变量要考虑:固定的和可移动的。固定变量包括局部变量和值类型,结构成员访问的结果值(其中的结构是固定的),指针间接寻址或指针成员访问。
可移动的变量包括引用类型ref和out参数、装箱变量和静态变量。下面的程序展示了如何使用fixed语句:
class FixedStatementDemo
{
unsafe static int strstr(string subString, string searchString)
{
int pos = 0;
bool found = false;
char* tmpPtr;

fixed (char* stringPtr = searchString)
{
char* charPtr = stringPtr;
for (int i = 0; i < searchString.Length; i++)
{
if (subString[0] != *charPtr++)
{
continue;
}
pos = i;
tmpPtr = charPtr;

for (int j = 1; j < subString.Length; j++)
{
found = true;

if (subString[j] != *tmpPtr++)
{
found = false;
pos = 0;
break;
}
}
if (found)
{
return pos;
}
}
}
return -1;
}

static void Main(string[] args)
{
string subString = "an";
string searchString = "banana";
int pos = strstr(subString, searchString);

if (pos == -1)
{
Console.WriteLine("'{0}' not found in '{1}'", subString, searchString);
}
else
{
Console.WriteLine("Found '{0}' in '{1}' at position {2}", subString, searchString, pos + 1);
}
}
}

注意:fixed语句是在strstr方法内。在调用strstr方法之前,把字符串固定是很方便的,然后再把它传送到指针,但是这违反使用fixed语句的一条重要规则:对象只能够固定必要的最短的时间。
当考虑固定变量的原因时,这条规则的原理是很简单的。固定的变量不能够被垃圾收集器收集。要防止固定变量被垃圾收集器收集,必须放置一些机制以辨识这个变量是固定的。如果对象没有固定,就会涉及不存在的开销。
因此,fixed语句是放置在strstr方法内,并且例程被优化以花费最少的时间来查找字符串内的子字符串。fixed语句把可移动的对象赋值到兼容类型的指针,如下所示:
fixed (char* stringPtr = searchString)
这就创建了一个只读指针。另外一个指针必须创建并赋予stringPtr的值来读取字符串的其余部分:
char* charPtr = stringPtr;
我们要以线性方式撕开搜索的字符串,因此要完成的第一件事是保持读取一直到第一个字符匹配为止。当字符不匹配时,跳开其他的循环的进程:
if (substring[0] != *charPtr++)
{
continue;
}
在整个例程中有类似的逻辑,但是关键点是只做必须的并且尽可能快的离开。

平台调用(PInvoke)
平台调用提供了C#程序执行本机代码的方法。当需要再利用遗留代码或者与其他没有可用接口的系统进行通信是有帮助的。一旦遗留代码被包围在DLL中,它就可以通过PInvoke与C#一道调用
PInvoke的另一个用途是访问现有的操作系统和第三方DLL。使用PInvoke就如同把方法原型申明为静态的外部方法并用DllImport属性来装饰它那么简单。如:
[DllImport("gdi32.dll")]
private static extern bool BitBlt(
IntPtr hdcDest, // handle to desination DC (device context)
int nXDest, // x-coord of destination upper-left corner
int nYDest, // y-corrd of destination upper-left corner
int nWidth, // width of destination rectangle
int nHeight, // height of destination rectangle
IntPtr hdcSrc, // handle to source DC
int nXSrc, // x-coordinate of source upper-left corner
int nYSrc, // y-coordinate of source upper-left corner
Int32 dwRop // raster operation code
);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐