一个简单的C#程序
C#要求所有的程序逻辑都包含在一个类型定义中。和C和C++不同,要在C#中创建全局函数或全局数据是不可能的。下面是一段简单的代码。
using System;
namespace MyApp {
class Program {
static void Main(string[] args) {
Console.WriteLine("Hello, world!");
}
}
}
在上面的代码中,我们在一个名为Program的类中定义了唯一的一个方法——Main()方法,它是一个可执行程序的入口,它的签名中使用了static关键字,说明它是类级别的,在调用之前不需要先创建对象(类的实例)。
在C#中作为程序入口的Main()方法有四种形式,因为它的返回类型可以是void、也可以是int(返回一个整数来告诉操作系统程序执行的状态,简单的说就是成功还是失败);而它可以没有参数,也可以有一个字符串数组的参数(用来接受命令行输入的参数)。因此,以下的四种写法都是可以作为程序入口的Main()方法的写法:
static void Main() {}
static void Main(string[] args) {}
static int Main() {}
static int Main(string[] args) {}
如果需要处理命令行参数,可以使用下面的代码:
static void Main(string[] args) {
for(int i = 0; i < args.Length; i++) {
Console.WriteLine("Arg: {0}", args[i]);
}
}
需要指出的是C#中的数组是System.Array类的别名,因此数组是对象,可以通过Length属性得到数组中元素的个数。作为标准for循环的替代方法,C#还提供了foreach循环,用法如下所示:
static void Main(string[] args) {
foreach(string str in args) {
Console.WriteLine("Arg: {0}", str);
}
}
除此之外,要处理命令行参数,还可以使用Environment类的静态方法GetCommandLineArgs(),该方法返回一个字符串数组,其中第一个字符串是应用程序本身,后面的是命令行参数,如下所示:
static void Main() {
string[] args = Environment.GetCommandLineArgs();
foreach(string s in args) {
Console.WriteLine("Args: {0}", s);
}
}
需要注意是的,使用这种方式时Main()方法不需要定义成带参数的,虽然这样做没有任何的坏处。Environment类代表了程序执行的环境,通过该类的属性和方法可以获得很多运行环境的信息,例如:通过MachineName属性可以获得机器名;通过UserName可以获得当前的用户名;通过ProcessorCount可以获得CPU的数量;通过OSVersion可以获得操作系统的版本信息。
System.Console类
顾名思义,Console类封装了基于控制台应用程序的输入、输出和错误流操作,下表是常用的成员:
成员 | 作用 |
Beep() | 强制控制台发出蜂鸣声 |
BackgroundColor | 设置控制台的背景颜色 |
ForegroundColor | 设置控制台的前景颜色 |
BufferHeight | 设置控制台缓冲区的高度 |
BufferWidth | 设置控制台缓冲区的宽度 |
Title | 设置控制台的标题 |
WindowHeight | 设置控制台窗口的高度 |
WindowWidth | 设置控制台窗口的宽度 |
WindowTop | 设置控制台窗口左上角的纵坐标 |
WindowLeft | 设置控制台窗口左上角的横坐标 |
Clear() | 清除缓冲区和控制台显示区域的内容 |
可以使用Console类进行基本的输入输出,如下所示:
using System;
class Program {
static void Main(string[] args) {
Console.WriteLine("***** It's a test of Console *****");
GetUserData();
}
static void GetUserData() {
Console.Write("Please enter your name: ");
string userName = Console.ReadLine();
Console.Write("Please enter your age: ");
string userAge = Console.ReadLine();
ConsoleColor oldColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Hello {0}! You are {1} years old.", userName, userAge);
Console.ForegroundColor = oldColor;
}
}
运行结果如下图所示:
如果要格式化控制台输出,可以使用如上例中使用的{0}、{1}之类的占位符标记嵌入字符串字面量中,在运行时,和占位符数量相等的值会传入到Console.WriteLine()方法中来替代每一个占位符。例如:
// 输出:10, 20, 30
Console.WriteLine("{0}, {1}, {2}", 10, 20, 30);
如果数值数据需要更精细的格式化,每个占位符中还可以包含不同的格式字符,如下表所示:
字符串格式字符 | 作用 |
C或c | 用于格式化货币(用本地货币符号为前缀) |
D或d | 用于格式化十进制数 |
E或e | 用于科学计数法 |
F或f | 用于定点小数的格式化 |
G或g | 将一个数格式化为定点或科学计数法格式 |
N或n | 用于基本的数值格式化(带逗号) |
X或x | 用于十六进制格式化 |
需要说明的是,.NET字符串格式化字符不仅局限于控制台,string类的静态方法Format也可以格式化,例如:
string msg = string.Format("The number is {0:x}", 255);
MessageBox.Show(msg); // 弹框显示The number is ff
系统数据类型和C#简化符号
和任何编程语言一样,C#定义了一组用于表示本地变量、成员变量、返回值以及输入参数的基本数据类型。但是和其他编程语言不同,这些关键字不只是简单的编译器可以识别的标记,C#的数据类型其实是System命名空间中完整的类型的简化符号,如下表所示:
简化符号 | 系统类型 | 范围 | 作用 |
bool | System.Boolean | true或false | 表示真或假 |
sbyte | System.SByte | -128~127 | 带符号8位整数 |
byte | System.Byte | 0~255 | 无符号8位整数 |
short | System.Int16 | -32768~32767 | 带符号16位整数 |
ushort | System.UInt16 | 0~65535 | 无符号16位整数 |
int | System.Int32 | -231~231-1 | 带符号32位整数 |
uint | System.UInt32 | 0~232-1 | 无符号32位整数 |
long | System.Int64 | -263~263-1 | 带符号64位整数 |
ulong | System.UInt64 | 0~264-1 | 无符号64位整数 |
char | System.Char | U+0000~U+FFFF | 16位的Unicode字符 |
float | System.Single |
| 32位浮点数 |
double | System.Double |
| 64位浮点数 |
decimal | System.Decimal |
| 128位带符号数 |
string | System.String | 表示一个Unicode字符集合,大小受到系统内存的限制 | |
object | System.Object | .NET世界中所有类型的基类(父类) |
所有的内建数据类型都支持默认构造器。简单的说就是可以用new关键字来创建变量,并将其赋值为默认值。
- bool类型的默认值为false
- 数值类型的默认值为0
- char类型的默认值为单个空字符
- BigInteger类型的默认值为0
- DateTime类型的默认值是1/1/0001 0:00:00
数据类型的层次结构如下图所示:
如果希望从字符串中解析出数值,可以参考下面的代码:
using System;
class Program {
static void Main(string[] args) {
bool b = bool.Parse("True");
double d = double.Parse("99.884");
int i = int.Parse("1234");
char c = char.Parse("w");
Console.WriteLine("{0}, {1}, {2}, {3}", b, d, i, c);
}
}
System中的DateTime和TimeSpan结构可以完成对时间日期的操作,这些类型虽然没有对应的关键字但却非常有用。请看下面的代码:
using System;
class Program {
static void Main(string[] args) {
DateTime dt1 = DateTime.Now;
DateTime dt2 = new DateTime(2001, 6, 22);
TimeSpan ts = dt1 - dt2;
Console.WriteLine(ts.Days); // 两个日期相隔天数
}
}
使用字符串
System.String提供了Length属性和很多的方法来帮助操作字符串,下表中给出了常用的成员。
成员 | 作用 |
Length | 返回当前字符串的长度 |
Compare() | 静态方法,比较两个字符串 |
Contains() | 判定当前字符串中是否包含一个指定的字符串 |
Equals() | 判定当前字符串是否与另一个字符串有相同的字符数据 |
Format() | 静态方法,用占位符和格式格式化一个字符串 |
Insert() | 在当前字符串指定位置插入字符串并返回新的字符串 |
PadLeft() | 在当前字符串左侧填充字符以使其达到指定长度并返回填充后的字符串 |
PadRight() | 在当前字符串右侧填充字符以使其达到指定长度并返回填充后的字符串 |
Remove() | 在当前字符串中指定位置删除指定长度的字符串并返回删除后的字符串 |
Replace() | 将当前字符串中指定的字符或字符串替换成新的字符或字符串并返回替换后的字符串 |
Split() | 将当前字符串用指定的字符拆分成多个字符串 |
Trim() | 修剪当前字符串左右两边的空白并返回修剪后的字符串 |
ToUpper() | 将当前字符串转成大写并返回 |
ToLower() | 将当前字符串转成小写并返回 |
字符串类型可以通过运算符+进行连接来构成更大的字符串,这个操作叫做字符串拼接(string concatenation)。+运算符会被编译器处理为String.Concat()方法调用。
与其他基于C的语言一样,在C#字符串字面量中可以包含各种转义字符,用来限制字符数据应该怎样输出到输出流中。转义字符以反斜杠(\)开始,如下表所示:
字符 | 作用 |
\’ | 单引号 |
\” | 双引号(因为字符串本来就以双引号开始和结束,因此字符串中如果包含双引号就需要此转义字符) |
\\ | 反斜杠(因为反斜杠本身是转义字符,因此字符串中如果包含反斜杠就需要此转义字符) |
\a | 蜂鸣 |
\n | 换行(在Windows平台上) |
\r | 回车 |
\t | 水平制表符 |
C#引入了以@为前缀的字符串字面量表示法,称之为逐字字符串()。在逐字字符串中可以使用任意字符而不必进行转义,如果不使用逐字字符串要表示一个路径就必须这样书写:
“c:\\Windows\\MyApp\\bin\\Debug”
而使用逐字字符串,可以这样书写:
@“c:\Windows\MyApp\bin\Debug”
也可以书写下面这样的字符串:
stringmyLongStr = @“It’s a very
very
very
long string.”;
在C#中判定字符串内容的相等性可以使用Equals()方法也可以使用==(通常情况下,该运算符对引用类型是比较其引用的是否为托管堆上的同一对象),C#为string类型重载了==运算符,因此它比较的也是字符串的值而不是内存中的引用。
关于string类型最重要的是知道它是不可变的,这就意味着对字符串的任何修改将产生新的字符串对象,例如之前提到的字符串的Remove、Insert、Replace、Trim等方法那样。
如果频繁的对string类型进行修改,它是非常低效额的。为此,.NET基础类库在System.Text命名空间下提供了StringBuilder类,它的对象代表一个可变字符串,通常对StringBuilder对象进行插入、修改、删除操作比string要高效得多。