名词解释
CLI Common Language Infrastructure 公共语言基础结构
CIL Common Intermediate Language 公共中间语言
BCL Base Class Library 基础类库
overload 重载:指的是同一个类 中有两个或多个名字相同但是参数不同 的方法,(注:返回值不能区别函数是否重载),重载没有关键字 。
override 重写 过载:指子类对父类中虚函数或抽象函数 的“覆盖”(这也就是有些书将过载翻译为覆盖的原因),但是这种“覆盖”和用new关键字来覆盖是有区别的。
new 覆盖:指的是不同类中(基类或派生类)有两个或多个返回类型、方法名、参数都相同,但是方法体不同的方法。但是这种覆盖是一种表面上的覆盖,所以也叫隐藏 ,被覆盖的父类方法是可以调用得到的。
HelloWorld 本书中的例子 1 2 3 4 5 6 7 class HelloWorld { static void Main () { System.Console.WriteLine("Hello World!" ); } }
其他例子 1 2 3 4 5 6 7 8 9 using System;class Hello { static void Main () { Console.WriteLine("Hello World!" ); } }
VS IDE自动生成例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace HelloWorld { class Program { static void Main (string [] args ) { Console.WriteLine("Hello World!" ); } } }
语法基础 标识符大小写风格 Pacal风格 (PascalCase/Pascal case)
每个单词首字母大写,例如:ComponentModel, Configuration, and HttpFileCollection. 注意在 HttpFileCollection中,由于首字母缩写词HTTP的长度超过两个字母,所以仅首字母大写。
camel风格 (camelCase/camel Case)
除了第一个字母小写,其他约定与Pascal大小写放个一样,例如:quotient, firstName, httpFileCollection, ioStream, and theDreadPirateRoberts.
形参与实参 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 namespace ConsoleApplication { class Program { static void Main (string [] args ) { int a = 5 ; abc(a); } static int abc (int c ) { return 5 ; } } }
Main
Main 方法是 C# 控制台应用程序或窗口应用程序的入口点。 (库和服务不要求将 Main 方法作为入口点。) 应用程序启动时,Main 方法是第一个调用的方法。
C# 程序中只能有一个入口点。 如果您有多个类都包含 Main 方法,则必须使用 /main 编译器选项编译您的程序,以指定用作入口点的 Main 方法。
不带参数
的Main1 2 3 4 5 6 7 8 class Hello { static void Main () { System.Console.WriteLine("Hello World!" ); System.Console.ReadKey(); } }
带参数
的Main1 2 3 4 5 6 7 8 class Hello { static void Main (string [] args ) { System.Console.WriteLine("Hello World!" ); System.Console.ReadKey(); } }
Main 概述
Main 方法是 .exe 程序的入口点,程序控制流在该处开始和结束。
Main 在类或结构内声明。 Main 必须是静态的,且不应该是 公用的。 (在前面的示例中,它接受默认访问级别 private。)但不要求封闭类或结构是静态的。
Main 的返回类型有两种:void 或 int。
所声明的 Main 方法可以具有包含命令行实参的 string[] 形参,也可以不具有这样的形参。 使用 Visual Studio 创建 Windows 窗体应用程序时,可以手动添加形参,也可以使用 Environment 类获取命令行实参。 形参读取为从零开始编制索引的命令行实参。 与 C 和 C++ 不同,不会将程序名称视为第一个命令行实参。
Main 例子 1 2 3 4 5 6 7 8 9 10 11 12 13 class Hello { static void Main (string [] args ) { System.Console.WriteLine("Hello World!" ); System.Console.WriteLine(args.Length); System.Console.WriteLine(System.Environment.CommandLine); System.Console.WriteLine(System.Environment.CurrentDirectory); System.Console.WriteLine(System.Environment.MachineName); System.Console.WriteLine(System.Environment.UserName); System.Console.ReadKey(); } }
编译上面代码,在控制台输入 ==HelloWorld.exe== 输出结果
Hello World!
0
helloworld.exe
D:\WaProj\Essential C#5.0, 4th Edition\第一章
DESKTOP-D10TF3C
ihome
编译上面代码,在控制台输入 ==HelloWorld== 输出结果
Hello World!
0
helloworld
D:\WaProj\Essential C#5.0, 4th Edition\第一章
DESKTOP-D10TF3C
ihome
单行多行语句 一行包含多条语句 1 System.Console.WriteLine("1" );System.Console.WriteLine("2" );
输出结果
1
2
一条语句跨越多行 1 2 System.Console.WriteLine( "Hello World!" );
输出结果
Hello World!
错误示例 1 2 System.Console.WriteLine("Hello World!" );
错误提示
HelloWorld.cs(5,31): error CS1010: 常量中有换行符
HelloWorld.cs(6,12): error CS1010: 常量中有换行符
缩进和空白
例子1
1 2 3 4 5 6 7 8 class Hello { static void Main () { System.Console.WriteLine("Hello World!" ); System.Console.ReadKey(); } }
例子2
1 class Hello {static void Main () {System.Console.WriteLine("Hello World!" );System.Console.ReadKey();}}
上面两个例子输出结果相同,对编译器来说无差别。
变量声明与赋值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Hello { static void Main () { string valerie; string max = "Have fun storming the castle!" ; valerie = "Think it will work?" ; System.Console.WriteLine(max); System.Console.WriteLine(valerie); max = "It would take a miracle." ; System.Console.WriteLine(max); string boys,girls; boys = girls = "We Are Young." ; System.Console.WriteLine(boys); System.Console.WriteLine(girls); System.Console.ReadKey(); } }
输出结果
Have fun storming the castle!
Think it will work?
It would take a miracle.
We Are Young.
We Are Young.
高级主题:字符串不可变 所有string类型数据,都是不可变的(或者说不可修改的),例如:不可能将字符串“We Are Yong.”修改为“We Are Old.”。也就是说,不能修改变量最初引用的数据,只能重新赋值,让它指向内存中的新位置。
控制台输入输出 从控制台获取输入 使用System.Console.ReadLine() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Hello { static void Main () { string firstName; string lastName; System.Console.Write("Enter your first name: " ); firstName = System.Console.ReadLine(); System.Console.Write("Enter your last name: " ); lastName = System.Console.ReadLine(); System.Console.WriteLine("Hello " + firstName + " " + lastName); System.Console.ReadKey(); } }
输出结果
Enter your first name: Jon
Enter your last name: Snow
Hello Jon Snow
高级主题:System.Console.Read()
System.Console.Read()方法返回的是与读取的字符值对应的证书,如果没有更多的字符可用,就返回-1。为了获取实际字符,需要先将证书转型为字符,代码如下:
代码示例 1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Hello { static void Main () { int readValue; char character; readValue = System.Console.Read(); character = (char ) readValue; System.Console.WriteLine(character); System.Console.ReadKey(); } }
System.Console.Read() 从标准输入流读取下一个字符。
System.Console.ReadKey() 获取用户按下的下一个字符或功能键。 按下的键显示在控制台窗口中。
按回车键之前,System.Console.Read()方法不会返回输入,即使用户输入了多个字符。
输入 123,按回车,再输入 a,输出结果
123
1
a
代码示例 2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Hello { static void Main () { int readValueFirst,readValueSecond,readValueThird; char character; readValueFirst = System.Console.Read(); character = (char ) readValueFirst; System.Console.WriteLine(character); readValueSecond = System.Console.Read(); character = (char ) readValueSecond; System.Console.WriteLine(character); readValueThird = System.Console.Read(); character = (char ) readValueThird; System.Console.WriteLine(character); System.Console.ReadKey(); } }
System.Console.Read() 从标准输入流读取下一个字符。
System.Console.ReadKey() 获取用户按下的下一个字符或功能键。 按下的键显示在控制台窗口中。
按回车键之前,System.Console.Read()方法不会返回输入,即使用户输入了多个字符。
输入 123,按回车,再输入 a,输出结果
123
1
2
3
a
上面两个示例,输入信息相同,由于代码不同因而输出结果不同。
输出到控制台
System.Console.Write() 输出后不添加换行符(当前行终止符)。 System.Console.WriteLine() 将参数内容(后跟当前行终止符)写入标准输出流,输出后,光标切换到下一行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Hello { static void Main () { string firstName; string lastName; System.Console.Write("Enter your first name: " ); firstName = System.Console.ReadLine(); System.Console.Write("Enter your last name: " ); lastName = System.Console.ReadLine(); System.Console.WriteLine( "Hello {0} {1}." , firstName, lastName); System.Console.ReadKey(); } }
示例代码中”Hello {0} {1}.”,标识了两个索引占位符,用于在字符串中插入数据。
输出结果
Enter your first name: Jon
Enter your last name: Snow
Hello Jon Snow.
交换索引占位符和对应变量
1 2 System.Console.WriteLine( "Hello {1}, {0}." , firstName, lastName);
输出结果
Enter your first name: Jon
Enter your last name: Snow
Hello Snow, Jon.
代码注释 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Hello { static void Main () { string firstName; string lastName; System.Console.Write( "Enter your first name: " ); firstName = System.Console.ReadLine(); System.Console.Write( "Enter your last name: " ); lastName = System.Console.ReadLine(); System.Console.WriteLine( "Hello {1}, {0}." , firstName, lastName); System.Console.ReadKey(); } }
目前观点
不要 使用注释,除非代码本身“一言难尽”。
要 尽量编写清晰的代码,而不是通过注释澄清复杂算法。
CIL和ILDAASM (公共中间语言和IL反汇编) C#编译器将C#代码转换成CIL代码而不是机器码。对于一个程序集(DLL文件或可执行文件),可以使用CIL反汇编程序将其析构成对应的CIL表示,从而查看其CIL代码。微软的反汇编程序文件:ILDASM(IL Disassembler),可以对程序或者类库执行反汇编,显示由C#编译器生成的CIL代码。
ildasm.exe存在与C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools类似位置
例子
ildasm /text hello.exe
输出结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 D:\WaProj\Essential C#5.0, 4th Edition\第一章>ildasm /text hello.exe // Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly Hello { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. .hash algorithm 0x00008004 .ver 0:0:0:0 } .module Hello.exe // MVID: {BDAC1292-8393-4BEB-9AD7-40DC171B0BF9} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // Image base: 0x02CF0000 // =============== CLASS MEMBERS DECLARATION =================== .class private auto ansi beforefieldinit Hello extends [mscorlib]System.Object { .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代码大小 19 (0x13) .maxstack 8 IL_0000: nop IL_0001: ldstr "Hello World!" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop IL_000c: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() IL_0011: pop IL_0012: ret } // end of method Hello::Main .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // 代码大小 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method Hello::.ctor } // end of class Hello // ============================================================= // *********** 反汇编完成 ***********************
/text 选项制定输出到命令控制台,而不是在ildasm的图形界面程序中显示。
常见.NET反汇编工具
为了减少程序被别人轻松反编译,可以考虑使用混淆器(obfuscator)产品。这些混淆器会打开IL代码,将代码加密成一种功能不变但更难于理解的形式。
结尾