第2课 初识C#开发环境
第3课 变量与常量
C#语言本身独有的特性决定了它功能的强大,读者在学习的过程中,应该首先掌握C#应用程序的目的,才能真正学习和了解这门语言。
究竟强大在什么地方?C#应用程序的目的是什么?
答:
sbyte,short,int,long,byte,ushort,uint,ulong,char,float,double,decimal,bool
C#中有无符号简单类型,这是Java中没有的。decimal类型也是Java中没有的,它有128位。
C#变量的常用类型有值类型和引用类型两大类。引用类型是C#的主要类型,在引用变量中保存的是对象的内存地址。
Java中也是值类型和引用类型,两者的设计是一样的。但那么我们有办法看到具体的内存地址吗?
答:
需要在委托中为引用类型变量分配内存。
是不是相当于在Java的堆中?委托和Java堆的设计差异有多大?
答:
委托类型:是一种数据结构,能够引用一个或多个方法。
委托究竟是一个什么东西?
答:
基本类型是编译器直接支持的类型。
string也是基本类型,感觉跟Java中的String一模一样。
object也是基本类型,它可以表示任何类型的值,跟Java中的Object类似。
变量名的第一个字符必须是字母、下划线或@。
第一个字符后的字符可以是字母、下划线或数字。但要注意,@后只能跟字母或下划线。
在Java中,@是不能用作变量的。
C#的常量类型只能是下列类型中的一种:值类型、string或枚举。
例
const double mm = 3.14;
const string nn = Liu;
这跟Java中有点不一样高,Java中关键字为final,但引用类型也可以为常量类型。
注意,byte不能隐式转换为char,任何类型都不能转换为bool,不管是在java还是在C#中。
C#中的值类型和引用类型从实质上讲是同源的,所以不但可以在值类型之间和引用类型之间的转换,也可以在值类型和引用类型之间转换。但是两者使用的内存类型不同,使它们之间的转换变得比较复杂。在C#中通常将值类型转换为引用类型的过程叫做装箱,将引用类型转换为值类型的过程叫做拆箱。
装箱和拆箱是整个C#类型系统的核心模块,在值类型和引用类型之间架起了一座沟通的桥梁。最终使任何值类型可以转换为object类型值,object类型值也可以转换为任何类型的值,并把类型值作为一个对象来处理。
我还以为可以像C/C++中那样随意转换呢,原来不是,虽然可以将任意类型转换到object,再转到值类型,这样能编译通过,但在运行时系统仍然会有类型检查,会抛异常的。例
Program1 s = new Program1();
object o = s;
int i = (int)o; // 抛异常
实际上,C#中的装箱拆箱就跟Java中int和Integer之间互转类似,无非是换了一个较怪的名字而已。
第4课 复杂的变量类型和.NET框架类
4.2.1 枚举
enum 枚举名称:整数值类型
{
枚举值1,
枚举值2,
......
枚举值n
}
注意,需要加类型,跟Java中不太一样哦!
4.2.2 结构
例
struct A
{
public int a;
public int b;
}
结构和类有什么差异?public,protected,private分别意味着什么?为什么不需要new,直接定义就可以了?
答:
结构不属于引用类型,只有引用类型才必须用new。
4.2.3 数组
和Java中数组类似,例
int[] arr = new int[5]{1,2,3,4,5};
for(int i = 0; i < arr.Length; i++)
Console.WriteLine(arr[i]);
4.3 基本.NET框架类
.NET框架类是整个.NET框架的核心,也是C#的基础。
实际上,.NET框架类就跟Java中的API类一样,你就知道有多重要了,有人说过,语言设计就是库设计。
Console
Convert
Math
第5课 表达式与运算符
new运算符的功能是创建引用类型的新实例,即创建类、数组和委托的新实例。
sizeof计算值类型所占用的字节数量,它只能对类型名进行操作,不能像C++那样对变量进行操作。
难道C#中的数组类型大小不是固定的吗,为什么不像Java那样把sizeof去掉?
答:
typeof运算符的功能是获取某类型的System.Type对象。同样,只能对类型名进行操作。
System.Type type = typeof(int);
System.Type用来干嘛?
答:
<<将指定数值向左移动指定位数,>>将指定数值向右移动指定位数。
对于左边空出来的位用0还是1补试验后和书上不一致,试验时如果是负数则用1补,书上好多地方都是错的,太烂了。
第6课 C#中的流程控制语句
int i = 5;
switch(i)
{
case 1:
Console.WriteLine(i);
break; // 必须加
case 5:
Console.WriteLine(i);
break;
default:
Console.WriteLine(i);
break; // 连这儿必须加
}
其他的if-else,while,do-while,for都是一样的,没什么说的。
C#中的常用跳转语句有break、continue、return和goto。
为什么还保留goto?
答:
第7课 面向对象编程技术
略,面向对象不是靠这几句话就懂的。
第8课 方法
C#方法的参数可以分为如下4类:
值参数:不使用任何修饰符声明,例
void Test3(int i)
{
i = 6;
}
相当于将实参拷贝给形参,之后就完全没关系了。
引用参数:使用ref修饰声明,例
void Test1(ref int i)
{
Console.WriteLine(i);
i = 4;
}
相当于引用,对形参修改会影响实参,即形参实际上实参的别名。
注意,调用时也要加ref,例Test1(ref i);
输出参数:使用out修饰声明,例
void Test(out int i)
{
i = 5;
Console.WriteLine(i);
i = 10;
}
没有设计为从外部获取值,而专门设计为向外部返回值,虽然形参也是实参的别名,但是对形参的第一次操作一定是赋值。
参数组:使用params修饰声明,例
void Test4(params int[] arr)
{
for (int i = 0; i < arr.Length; i++ )
{
Console.WriteLine(arr[i]);
arr[i]+= 1;
}
}
这样表示参数可以传递一个数组,和void Test4(int[] arr)的区别是:
除了可以这样调用
int[] arr = new int[2] { 1, 2};
Test4(arr);
还可以
Test4(1, 2);
其实数组没有特殊的,跟Java中一样,把它看做一个普通的引用类型就OK。
方法Main是C#程序的入口点,Main结束后,程序也就结束了。
应用程序内可能包含多个Main方法,这时,必须使用外部机制来设置一个Main方法作为程序的入口。
这一点跟Java差不多,可以通过在项目属性的应用程序->启动对象中设置。
第9课 C#类基础
C#内的一切类型都可以看做是类,并且所有的语句都位于类内。
同Java一样,是纯面向对象语言。
类成员访问修饰符
public 设置访问不受限制,可在类内和任何类外的代码中访问
protected 设置可访问限定于类内或从该类派生的类
internal 设置可访问域限定于类所在项目内
protected internal 即表示protected或internal,设置可访问域限定于类所在的项目或那些由它的类派生的类内
private 设置可访问域限定于它所属的类内
这5种访问修饰符都可用于修饰类内的成员。默认是private。
静态成员必须通过类名来引用,而不能通过对象来引用。
这样的好处是消除了歧义,一看使用方式就知道是静态成员。
在C#中,通常将不在类内声明的类叫做顶级类。顶级类只能使用public和internal两种访问修饰符。
Java中的基本单位为包,虽然一个插件中可以包含多个包,但在导出的时候,也是按包导出的,所以说基本单位为包。如果顶级类加了public,则包外可见。如果默认不加的话,则只能包内可见。
其实在C#中,道理和Java类似,只不过基本单位变为项目了。public项目外可见(相当于包外可见),而默认internal则只能项目内可见(相当于包内可见)。
C#中一个项目要调用另一个项目(在解决方案上添加->新建项目->windows->类库)更加简单,只需要放到一个解决方案内,然后从项目的右键菜单中添加引用就行了。
类还可以用abstract、sealed修饰。
分别代表什么?
第10课 深入C#类
隐式的构造函数是public类型的,显式的跟普通方法一样,默认是private的。
class Class1
{
static Class1() // 不能添加访问修饰符,最多只执行一次。对Class1的任何访问都会触发
{
Console.WriteLine("static constructor");
}
public Class1() // 每个实例调用一次
{
Console.WriteLine("common constructor");
}
}
静态构造函数就相当于Java中的
static{
......
}
析构函数是C#的特殊函数之一,其功能是用于销毁这个实例所占的内存,但是调用时机由公共语言运行时的垃圾回收机制确定,所以最好不要利用C#的析构函数来回收资源。
看来析构函数相当于Java中的finalize方法。
使用关键字readonly修饰的字段被称为只读字段,而使用关键字const修饰的字段被称为常量字段。
对于常量和只读字段,有如下3点区别:
常量只能在声明时赋值,常量的值在编译时就已经确定,在程序中根本不能改变;而只读字段可以在声明时或者在构造函数内赋值,只读字段的值不能在编译使确定,而是在运行时确定的。
常量的类型只能是值类型,string或者枚举类型(其实引用类型赋值null也可以);而只读变量可以是任何类型。
属性定义
class Rect
{
private int width = 0; // 字段
private int height = 0;
public int Width // 属性,有点类似对应字段的get/set函数,但访问时可以像访问字段一样访问
{
get
{
return width;
}
set
{
if (value> 0)
width = value;
else
Console.WriteLine("invalid width");
}
}
public int Area // 属性,只有get,但没有对应的字段,注意体会
{
get
{
return width* height;
}
}
}
属性使用
Rect rect = new Rect();
Console.WriteLine(rect.Area);
rect.Width =10; // 可以像访问字段一样访问属性
属性是字段的扩展,注意体会。
10.7 索引器
class MyTest
{
public const int MaxNum = 8;
private string[] member = new string[MaxNum];
public string this[int idx] // 索引器的名称必须是this
{
get
{
return member[idx];
}
set
{
member[idx]= value;
}
}
}
使用时
MyTest ar = new MyTest();
ar[0] = "0";
ar[1] = "1";
Console.WriteLine(ar[1]);
索引器又被称为带参数的属性。
使用起来怪怪的,如果这个对象用来代表一个数组的话,还有点意思,但是如果对象中包含多个数组的话,那该怎么办呢?
10.10 分部类
在单个文件中维护某个类的所有源代码是程序员需要遵循的编程习惯,但是有时一个类会变得非常大和复杂。在上述情况下,单个文件维护就成为一种不切实际的幻想。此外,程序员经常使用源代码生成器来生成应用程序的初始结构,然后修改得到代码。遗憾的是,当将来某个时候再次用代码生成器来生成源代码时,已有的修改会被源代码生成器改写或者删除。分部类可以将一个类划分为多个部分,存储在不同的源文件中,以便于开发和维护。
定义
partial class Test
{
private int i = 8;
}
partial class Test
{
public void Print()
{
Console.WriteLine(i);
}
}
使用
Test t = new Test();
t.Print(); // 8
第11课 C#集合、数组和集合类
第12课 foreach语句、自定义集合类和迭代器
第13课 继承和多态基础
第14课 接口和类转换
第15课 字符串与正则表达式
第16课 委托与事件
第17课 泛型
第18课 C#文件操作和流文件操作
第19课 XML文件操作处理
第20课 Windows窗体编程基础
第21课 菜单、工具栏和对话框
第22课 Web应用编程
第23课 数据库和ADO.NET操作
第24课 DataGrid和数据绑定
第25课 GDI+图形图像编程
第26课 C#非托管代码操作
第27课 水晶报表应用
第28课 DotNetBar控件应用
第29课 Microsoft Enterprise Library应用
第30课 水晶报表应用
第31课 应用MapXtreme地图控件
第32课 项目开发案例:俄罗斯方块游戏开发
第33课 项目开发案例:在线留言本
////////////////////////////////////////////////////////////////////////////////////////////////////////
C# 语言和 .NET Framework 介绍
C# 是一种简洁、类型安全的面向对象的语言,开发人员可以使用它来构建在.NET Framework 上运行的各种安全、可靠的应用程序。使用 C#,您可以创建传统的 Windows 客户端应用程序、XML Web services、分布式组件、客户端- 服务器应用程序、数据库应用程序以及很多其他类型的程序。
C# 语言
C# 语法表现力强,只有不到 90 个关键字,而且简单易学。C# 的大括号语法使任何熟悉C、C++ 或 Java 的人都可以立即上手。了解上述任何一种语言的开发人员通常在很短的时间内就可以开始使用 C# 高效地工作。C# 语法简化了 C++ 的诸多复杂性,同时提供了很多强大的功能,例如可为空的值类型、枚举、委托、匿名方法和直接内存访问,这些都是Java 所不具备的。C# 还支持泛型方法和类型,从而提供了更出色的类型安全和性能。C# 还提供了迭代器,允许集合类的实现者定义自定义的迭代行为,简化了客户端代码对它的使用。
作为一种面向对象的语言,C# 支持封装、继承和多态性概念。所有的变量和方法,包括Main 方法(应用程序的入口点),都封装在类定义中。类可能直接从一个父类继承,但它可以实现任意数量的接口。重写父类中的虚方法的各种方法要求 override关键字作为一种避免意外重定义的方式。在 C# 中,结构类似于一个轻量类;它是一种堆栈分配的类型,可以实现接口,但不支持继承。
除了这些基本的面向对象的原理,C# 还通过几种创新的语言结构加快了软件组件的开发,其中包括:
封装的方法签名(称为委托),它实现了类型安全的事件通知。
属性 (Property),充当私有成员变量的访问器。
属性 (Attribute),提供关于运行时类型的声明性元数据。
内联 XML 文档注释。
在 C# 中,如果需要与其他 Windows 软件(如 COM 对象或本机Win32 DLL)交互,可以通过一个称为“Interop”的过程来实现。互操作使 C# 程序能够完成本机 C++ 应用程序可以完成的几乎任何任务。在直接内存访问必不可少的情况下,C#甚至支持指针和“不安全”代码的概念。
C# 的生成过程比 C 和 C++ 简单,比 Java 更为灵活。没有单独的头文件,也不要求按照特定顺序声明方法和类型。C#源文件可以定义任意数量的类、结构、接口和事件。
.NET Framework 平台体系结构
C# 程序在 .NET Framework 上运行,它是Windows 的一个必要组件,包括一个称为公共语言运行时 (CLR) 的虚拟执行系统和一组统一的类库。CLR 是 Microsoft 的公共语言基础结构(CLI) 的一个商业实现。CLI 是一种国际标准,是用于创建语言和库在其中无缝协同工作的执行和开发环境的基础。
用 C# 编写的源代码被编译为一种符合 CLI 规范的中间语言(IL)。IL 代码与资源(如位图和字符串)一起作为一种称为程序集的可执行文件存储在磁盘上,通常具有的扩展名为.exe 或 .dll。程序集包含清单,它提供关于程序集的类型、版本、区域性和安全要求等信息。
执行 C# 程序时,程序集将加载到 CLR 中,这可能会根据清单中的信息执行不同的操作。然后,如果符合安全要求,CLR执行实时 (JIT) 编译以将 IL 代码转换为本机机器指令。CLR 还提供与自动垃圾回收、异常处理和资源管理有关的其他服务。由 CLR 执行的代码有时称为“托管代码”,它与编译为面向特定系统的本机机器语言的“非托管代码”相对应。下图演示了 C# 源代码文件、基类库、程序集和 CLR 的编译时与运行时的关系。
语言互操作性是 .NET Framework 的一个关键功能。因为由C# 编译器生成的 IL 代码符合公共类型规范 (CTS),因此从 C# 生成的 IL 代码可以与从 Visual Basic、Visual C++、VisualJ# 的 .NET 版本或者其他 20 多种符合 CTS 的语言中的任何一种生成的代码进行交互。单一程序集可能包含用不同 .NET 语言编写的多个模块,并且类型可以相互引用,就像它们是用同一种语言编写的。
除了运行时服务,.NET Framework 还包含一个由4000 多个类组成的内容详尽的库,这些类被组织为命名空间,为从文件输入和输出到字符串操作、到 XML 分析、到 Windows 窗体控件的所有内容提供多种有用的功能。典型的C# 应用程序使用 .NET Framework 类库广泛地处理常见的“日常”任务。
1.
Main 方法是驻留在类或结构内的静态方法。它可以有参数也可以没有参数,它可以返回int也可以返回void。
当Main 方法的参数是 string 数组时,该数组表示用于激活程序的命令行参数。请注意,不像C++,该数组不包含可执行 (exe) 文件名。
那么如何才能获取可执行文件的路径呢?
2.多维数组和交错数组的区别?
int[,] array = new int[4, 2];
表示有4行2列的数组。
int[, ,] array1 = new int[4, 2, 3];
表示一个三维数组。
int[][] jaggedArray = new int[3][];
表示一个有3个元素的一维数组,而每个元素又是一个数组。
交错数组是元素为数组的数组。交错数组元素的维度和大小可以不同。交错数组有时称为“数组的数组”。(我觉得在C++中的指针数组就相当于交错数组了,比如int*a[3];)
而多维数组跟C++中的多维数组一样,但是元素数组的大小必须一样。
在C#中,还是把数组看做是普通的引用类型(即自定义类)更好理解,或者干脆认为只有数组类型,而不同维数的数组相当于不同引用类型。
3.字符串
@ 符号会告知字符串构造函数忽略转义符和分行符。因此,以下两个字符串是完全相同的:
string p1 = "\\\\My Documents\\MyFiles\\";
string p2 = @"\\My Documents\My Files\";
有了这个功能,就可以直接在代码和浏览器之间拷贝路径使用了,方便很多。
4.C# 允许用户定义的类型通过使用 operator 关键字定义静态成员函数来重载运算符。
5.C# 允许程序员在类或结构上声明转换,以便类或结构与其他类或结构或者基本类型进行相互转换。
class Program1
{
int i = 0;
public static explicit operator Program1(int i)
{
// code to convert from int to SampleClass...
Program1 temp = new Program1();
temp.i = i;
return temp;
}
static void Main(string[] args)
{
Program1 p = (Program1)10; // 如果没有定义转换函数的话,怎么都是转不过去的
Console.ReadKey();
}
}
6.若要检查引用相等性,应使用 ReferenceEquals。若要检查值相等性,应使用Equals。
运算符 == 的重载中的常见错误是使用(a == b)、(a == null) 或 (b == null) 来检查引用相等性。这会导致调用重载的运算符 ==,从而导致无限循环。应使用ReferenceEquals 或将类型强制转换为 Object 来避免无限循环。
7.结构可视为轻量类,是创建用于存储少量数据的数据类型的理想选择,不能表示以后可能要通过继承进行扩展的类型。
结构具有以下特点:
结构是值类型,而类是引用类型。所以可以直接像值类型那样定义在栈中。
向方法传递结构时,结构是通过传值方式传递的,而不是作为引用传递的。跟值类型一样,赋值时都是值拷贝。
与类不同,结构的实例化可以不使用 new 运算符。当然使用也行。
结构可以声明构造函数,但它们必须带参数。
一个结构不能从另一个结构或类继承,而且不能作为一个类的基。所有结构都直接继承自System.ValueType,后者继承自 System.Object。
结构可以实现接口。
在结构中初始化实例字段是错误的。
8.类可以从其他类中继承。这是通过以下方式实现的:在声明类时,在类名称后放置一个冒号,然后在冒号后指定要从中继承的类(即基类)。
public class B : A
{
publicB() { }
}
注意,去掉了C++中的公有,保护,私有继承,同时也去掉了java中的extends,想想有什么好处?
9.使用 abstract 关键字可以创建仅用于继承用途的类和类成员,即抽象类。
public abstract class A
{
publicabstract void DoWork(int i); // 加了abstract就不能有函数体,否则会编译错误
}
可以将类声明为密封类。方法是在类定义中将关键字 sealed 置于关键字 class 的前面。例如:
public sealed class D
{
// Classmembers here.
}
密封类不能用作基类。因此,它也不能是抽象类。密封类主要用于防止派生。由于密封类从不用作基类,所以有些运行时优化可以使对密封类成员的调用略快。
在对基类的虚成员进行重写的派生类上的类成员、方法、字段、属性或事件可以将该成员声明为密封成员。在用于以后的派生类时,这将取消成员的虚效果。方法是在类成员声明中将sealed 关键字置于 override 关键字的前面。例如:
public class D : C
{
publicsealed override void DoWork() { }
}
例
class A
{
public virtual void DoWork(int i) // 定义为虚函数,那么子类就可以通过override关键字来重写该函数
{
Console.WriteLine("A");
}
}
class B : A
{
public override void DoWork(int i) // 如果加了override,则之前DoWork一定有virtual标记,否则编译不过的
{
Console.WriteLine("B");
}
}
class Program1
{
static void Main(string[] args)
{
A a = new B();
a.DoWork(10);
Console.ReadKey();
}
}
注意,这个地方和Java中区别较大,Java中直接全都是相当于是虚函数。而C#中需要virtual和override两个关键字才能达到虚函数效果。
10.接口具有下列属性:
接口类似于抽象基类:继承接口的任何非抽象类型都必须实现接口的所有成员。
不能直接实例化接口。
接口可以包含事件、索引器、方法和属性。
接口不包含方法的实现。
类和结构可从多个接口继承。
接口自身可从多个接口继承。
11.如果您的应用程序在使用昂贵的外部资源,则还建议您提供一种在垃圾回收器释放对象前显式地释放资源的方式。可通过实现来自IDisposable 接口的 Dispose 方法来完成这一点,该方法为对象执行必要的清理。这样可大大提高应用程序的性能。即使有这种对资源的显式控制,析构函数也是一种保护措施,可用来在对Dispose 方法的调用失败时清理资源。(跟Java类似)
12.委托具有以下特点:
委托类似于 C++ 函数指针,但它是类型安全的。
委托允许将方法作为参数进行传递。
委托可用于定义回调方法。
委托可以链接在一起;例如,可以对一个事件调用多个方法。
方法不需要与委托签名精确匹配。有关更多信息,请参见协变和逆变。
C# 2.0 版引入了匿名方法的概念,此类方法允许将代码块作为参数传递,以代替单独定义的方法。
例
namespace ConsoleApplication1
{
class A
{
public int Add(int x, int y)
{
return x + y;
}
static public int Sum(int x, int y)
{
return x + y;
}
}
class Program1
{
delegate int PerformCalculation(intx, int y);
static public int Add(int x, int y)
{
return x + y;
}
static void Main(string[] args)
{
PerformCalculation delegate1= Add;
Console.WriteLine(delegate1(4, 5));
PerformCalculation delegate2= (new A()).Add;
Console.WriteLine(delegate2(4, 5));
PerformCalculation delegate3= A.Sum;
Console.WriteLine(delegate3(4, 5));
}
}
}
13.匿名方法
例1
// Create a handler for a click event
button1.Click += delegate(System.Object o,System.EventArgs e)
{ System.Windows.Forms.MessageBox.Show("Click!"); };
例2
void StartThread()
{
System.Threading.Thread t1 = new System.Threading.Thread
(delegate()
{
System.Console.Write("Hello,");
System.Console.WriteLine("World!");
});
t1.Start();
}
14.委托和函数模板结合起来
public delegate void Del
public void Notify(int i) { }
15.合并委托
Del a, b, c, d;
// Create the delegate object a that references
// the method Hello:
a = Hello;
// Create the delegate object b that references
// the method Goodbye:
b = Goodbye;
// The two delegates, a and b, are composed toform c:
c = a + b;
此时调用C的话,相当于调用a和b,虽然道理类似和以前搞的注册反注册类似,但实现看起来更简单。
16.事件具有以下特点:
事件是类用来通知对象需要执行某种操作的方式。
尽管事件在其他时候(如信号状态更改)也很有用,事件通常还是用在图形用户界面中。
事件通常使用委托事件处理程序进行声明。
事件可以调用匿名方法来替代委托
17.C#语言允许事件使用任何委托类型,但 .NET Framework 对委托和事件有更严格的准则。如果打算将您的组件与“.NET Framework”一起使用,您可能希望遵守这些指南。
.NET Framework 指南指示用于事件的委托类型应采用两个参数:“对象源”参数(用于指示事件源)和特定于事件的参数(它封装有关事件的其他任何信息)。特定于事件的参数应从EventArgs 类派生。对于不使用任何附加信息的事件,.NET Framework 提供了 EventHandler 类。
也就是说事件是委托的一种特殊形式。
18.泛型概述
使用泛型类型可以最大限度地重用代码、保护类型的安全以及提高性能。
泛型最常见的用途是创建集合类。
.NET Framework 类库在 System.Collections.Generic 命名空间中包含几个新的泛型集合类。应尽可能地使用这些类来代替普通的类,如System.Collections 命名空间中的 ArrayList。
您可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。
可以对泛型类进行约束以访问特定数据类型的方法。
关于泛型数据类型中使用的类型的信息可在运行时通过反射获取。
19.在泛型类和泛型方法中产生的一个问题是,在预先未知以下情况时,如何将默认值分配给参数化类型 T:
T 是引用类型还是值类型。
如果 T 为值类型,则它是数值还是结构。
给定参数化类型 T 的一个变量 t,只有当 T 为引用类型时,语句 t = null 才有效;只有当T 为数值类型而不是结构时,语句 t = 0 才能正常使用。解决方案是使用 default 关键字,此关键字对于引用类型会返回空,对于数值类型会返回零。对于结构,此关键字将返回初始化为零或空的每个结构成员,具体取决于这些结构是值类型还是引用类型。
例
public T GetNext()
{
T temp = default(T);
// ...
returntemp;
}
20.C++ 模板和 C# 泛型之间的区别
略,毕竟C++的模板太复杂了
21.迭代器
略
22.可空类型
可空类型是 System.Nullable 结构的实例。可空类型可以表示其基础值类型正常范围内的值,再加上一个null 值。例如,Nullable
23.为了保持类型安全,默认情况下,C# 不支持指针运算。不过,通过使用 unsafe 关键字,可以定义可使用指针的不安全上下文。
不安全代码具有下列属性:
方法、类型和可被定义为不安全的代码块。
在某些情况下,通过移除数组界限检查,不安全代码可提高应用程序的性能。
当调用需要指针的本机函数时,需要使用不安全代码。
使用不安全代码将引起安全风险和稳定性风险。
在 C# 中,为了编译不安全代码,必须用 /unsafe 编译应用程序。
24.
在 Visual C# 中,可以为代码创建文档,方法是在 XML 标记所指的代码块前面,直接在源代码的特殊注释字段中包括XML 标记。例如:
///
/// Thisclass performs an important function.
///
public class MyClass{}
使用 /doc 进行编译时,编译器将在源代码中搜索所有的 XML 标记,并创建一个 XML 文档文件。
注意
XML doc 注释不是元数据;它们不包括在编译的程序集中,因此无法通过反射对其进行访问。
25.应用程序域
26.程序集和全局程序集缓存
27.互操作性
例
using System.Runtime.InteropServices;
public class Win32 {
[DllImport("user32.dll",CharSet=CharSet.Auto)]
public static extern int MessageBox(int hWnd, String text,
Stringcaption, uint type);
}
public class HelloWorld {
public static void Main() {
Win32.MessageBox(0, "Hello World", "PlatformInvoke Sample", 0);
}
}
例
using System;
using System.Runtime.InteropServices;
public delegate bool CallBack(int hwnd, int lParam);
public class EnumReportApp
{
[DllImport("user32")]
public static extern int EnumWindows(CallBackx, int y);
public static void Main()
{
CallBack myCallBack= new CallBack(EnumReportApp.Report);
EnumWindows(myCallBack,0);
}
public static bool Report(int hwnd, int lParam)
{
Console.Write("Window handle is ");
Console.WriteLine(hwnd);
return true;
}
}
通过上面两个例子,C#想要调用非托管DLL中的函数,并不是想C++中那样包含头文件就直接调用那样轻松。还有就是如何调用输出的C++类?
28.反射
29.C# DLL