C#的三大难点之一:byte与char,string与StringBuilder
2015-11-08 20:03阅读:
相关文章:
C#的三大难点之前传:什么时候应该使用C#?
C#的三大难点之一:byte与char,string与StringBuilder
C#的三大难点之二:托管与非托管
C#的三大难点之三:消息与事件
一、byte与char
byte
首先,我们讲一下C#中的byte类型。
JAVA中也有byte类型,和C#中的byte类型类似。
而C/C++中没有byte的基本类型,使用BYTE关键字。
typedef unsigned char
BYTE
C#中的byte,与C++中的unsigned
char类似,都是存储一个0-255的数。(而C++中char的取值范围为-128-127。)
C++中与C#中,都可以强制转换为int类型。
例:
//C++ char c = 'a';
int i = (int) c; // i =
97
//C# byte c =
97;
int a = (
int)c;
// i =
97
与C++中的char区别在于:
byte类型本质上只是一个数值,并不能代表一个字母。
因此,在C#中,类似byte c = ‘a’是错的。
同样,在C#中,如果你查看一个byte[]的内容,你只能看到一个0-255的值。而在C++中,查看一个char[]的内容时,你可以看到每个char所代表的字母。例如:
char ca[3] = {'a',
'b', 'c'}; char
ca2[4] = 'abc'; //This is also
right: //char ca2[] = 'abc';

byte[] ba = {97, 98,
99}; //or byte[] ba = new byte[]{97, 98,
99};

注意两种数组定义方式的区别:C#定义的类型是byte[]。
另外注意一个string是以\0结尾的。所以直接用string赋值给char[]要多一位。
由于byte只表示数值,因此,只有指定了编码方式,才能将byte转化为一个可见的字符串。此外,你不能通过任何方式输出字符串:既没有类似C++中printf(%s)的用法,也不能直接byte[].toString()。
如果想转化为字符串,需要给定编码,并进行如下调用:
Encoding.ASCII.GetString(ba);
但是,这个转换也同样存在着问题:
由于ASCII编码的范围是0-127,因此,对于byte[]中大于127的值,在转换之后会直接变为字符’?’。
编码的问题又是一个很复杂的问题了,本篇文章只是带过,不做详细讲解。
由于byte不能直接显示为字符,因此,byte并不是作为字符的存储格式,而是常用于
存储数据流,例如:
-
通信中的数据包。由于C#和C++可能采用不同的编码方式,因此用纯数值的byte传输,可以避免通信过程中编码的问题。C#中通信类NetworkStream的函数Read()和Write()的声明分别为:
public override int
Read(byte[] buffer,
int offset, int size);
public override void
Write(byte[] buffer,
int offset, int
size);
-
加解密的数据。C#中,加解密类DESCryptoServiceProvider中的函数TransformFinalBlock()的声明为:
byte[] TransformFinalBlock(byte[]
inputBuffer, int inputOffset, int
inputCount)
- 调用C/C++编写的DLL,对于char*,在C#中可以采用byte[]接收。这一点具体会在下一段介绍。
char
C#中的char与C++中的char也是不同的。
这是因为编码方式存在着不同,C#中采用Unicode编码,因此,char的取值范围为0-65535。如下代码是正确的
char c =
(char)0x4e00;
这段代码的结果是中文汉字“一”(Unicode编码19968)。实际上,这段代码在C++中也是正确的,但是,C++会将这个数字截断为char的取值范围(-127-128),然后输出为\0。
正由于C#中的char与C++中不是完全对应的,因此,当C#调用C/C++编写的DLL时,不能用char[]与对应C/C++中的char*类型,而是要根据实际情况选择byte,string或StringBuilder。
-
如果DLL中char*不是用来存储ASCII字符,而是用作buffer(即可能出现0-127以外的值),C#端应该用byte[]。
- 如果DLL中char存储一般的ASCII字符,且参数用作输入,C#端应该用string。
-
如果DLL中char存储一般的ASCII字符,且参数用作输出,C#端应该用StringBuilder。其原因在后文会有叙述。
因为此时char*的长度是不确定的,可以用类似
StringBuilder sb = new
StringBuilder(Constants.MAXLEN);
达到类似C++中
char* cp = new
char[MAXLEN];
及C中
char* cp = (char
*)malloc(sizeof(char) *
MAXLEN);
的效果。
StringBuilder的变长特性在后面会有详细介绍。
二、string与stringBuilder
JAVA中也存在同名类型,用法与C#类似。
在C++中,操作字符串的方式有两种:C风格字符串(继承自C,强制以\0结尾的char*)和string。
(实际上,string类也是以char*为基础的。如果自己写string类,操作的基本类型就是char[]。参见
《程序员面试宝典》10.5:拷贝构造函数和赋值函数的第一题。)
string相对于C风格字符串有了一定的改进,比如强制以\0结尾,以及不需要额外处理字符串所占的内存。
《C++
Primer》中4.3:C风格字符串举了一个拼接字符串的例子,在C风格字符串中,需要先仔细考虑每个字串所占的内存,再使用strcat或strncat。
char largeStr[16 + 18
+ 2]; // to hold cp1 a space and cp2
strncpy(largeStr, cp1, 17); // size
to copy includes the null strncat(largeStr,
' ', 2); // pedantic, but a good
habit strncat(largeStr, cp2, 19);
// adds at most 18 characters, plus a
null
而用string的话,我们只需要简单使用
string largeStr = cp1; // initialize large
Str as a copy of cp1 largeStr += ' '; //
add space at end of largeStr largeStr += cp2; //
concatenate cp2 onto end of largeStr
即可。
但是,string仍然有其局限性。比如,上面举的字符串拼接的例子,实际上拼接得到的是一个新的字符串largeStr,而原有的字符串cp1和cp2仍然存在,这在对字符串进行大量修改的场合,会导致严重的资源浪费。
而C#中采用StringBuilder解决这个问题。对StringBuilder的操作,如拼接Append(),插入Insert(),删除Remove(),替换Replace()(不过string也可以用s[0]
= ‘a’的方式替换),都是对现有的字符串进行操作,而不会引入新的字符串,避免了新建string类所造成的系统开销。
那么,在C#调用C的DLL时,为什么对于作为输出的参数要用StringBuilder呢?
因为string
不能改变自身的值,如果用string的话,函数调用之后string的值不会发生改变。
一个例子:
DLL中
void CallFromDLL(char* cp) {
printf (cp); printf ('');
*cp='a'; printf (cp); // If in C#
we use string, cp can also be changed here
printf (''); }
C#中
[DllImport(@'TestLib.dll', CallingConvention =
CallingConvention.Cdecl)] //public static extern void
CallHelloFromDLL(string s); public
static extern void
CallHelloFromDLL(StringBuilder s);
static void
Main() {
Console.WriteLine('This is C# program');
//string s = new string('0', 100);
//CallFromDLL(s); //Console.WriteLine(s);
StringBuilder sb = new
StringBuilder(100); CallFromDLL(sb);
Console.WriteLine(sb); }
此外,JAVA中还有一个类型为StringBuffer(C#没有),是为了解决StringBuilder线程不安全的问题,此处不再赘述。