C# 它通过底层的虚拟机机制减少了 C/C++ 语言中常常容易发生的内存泄漏和安全性问题,它代价是运行速度相对减慢
C# 则编译成中间语言, 它与 Java 在字节代码上有些相似。而 C++ 经常把代码编译成汇编语言。之后 IL 在通过 Iust-In-Time 编译进程转换成本机的可执行代码
C# 将开发人员从记账式的内存管理任务中解放出来,不再需要显式地删除动态分配的给堆的内存,而 C++ 则要这么做,无用存储单元收集器将周期性地清空不再使用的内存
C# 中指针的运用可能与 C++ 一样,但它只用于已特别标记为应用指针的代码块中。对于大部分的程序,C# 依赖 VB/Java 风格的对类实例的引用,而不需和 C++ 一样频繁地使用指针
在 C++ 比 C# 中用来显式重载的运算符比要多,主要是 C# 编译器是运用一些定制的基本操作符重载(如=)来自动计算出组合操作符的重载(如+=)
C++ 依赖于标准库,C# 依赖于.NET 基类。而 .NET 基类是以单一的继承为基础,而标准库是以继承和模板为基础
C# 是基于 GUI 的环境下(不仅仅是 Windows 环境,尽管现在只是在 Windows 中可用)专门为编程和背景服务(如 Web 服务)而设计的。这与语言本身无关,而是反映在基类库的设计中
C# 包含一些预处理指令,它的语法和 C++ 一样。但 C# 的预处理指令少得多,因为 C# 的其它语言特性使得这些指令不再重要
C# 的枚举比 C++ 中枚举的功能更为广泛。它们在权限范围内的语法结构很成熟,可支持不同的属性与方法。枚举仍是作为基本的数字类型来执行的,所以不会存在性能上的损失。
当析构函数被调用后,C# 不能保证它的执行, 除非要除空的是具体的外部源代码,如文件与数据库连接,C# 不可以在析构函数中放置代码,而 C++ 则可以。
C# 正式区分了类(一般用于包含许多方法的大型对象)和结构(一般用于只包含变量集合的小型对象)类和结构的存储方式不同,结构不支持继承。
C# 不支持函数指针。但委托可以实现对应的功能,它把引用以一种特殊的形式封装到方法中。委托可以在方法之间传递,用于调用包含引用的方法。这与 C++ 指针的工作方式相同
事件与委托相似,但它支持回调模式。就是当执行一些操作时,客户通知机器,将这些操作通知给它。工作方式与 VB 相同。 特性:这一概念在 VB 和COM 中应用的很广,C# 中也导入了这一概念
可以将接口看为是一个抽象的类,其目的是用来定义类同意执行的方法和属性。C# 接口与 COM 接口不同 ; C# 接口是简单的方法列表,而 COM 接口有其它的相关的特性,如GUIDS,但他们的原理基本相同。
C# 可以用特性元信息(如属性)来修饰类、方法和参数等。可以在运行时内访问属性,已决定代码的执行。
C#的lock语句可支持线程同步(C++不支持线程,必须在代码中通过调用API或其它的类库来实现)
C#中,代码可自动获得已编译的装配件(库和可执行文件)中的类定义的信息。可以编写显示类和方法信息的程序
C#适合企业各应用程序,C++适合底层开发(游戏等)
另外关于 C# 的语法以及数据结构 会在下一篇文章进行总结,接下来看看 C# 这门语言的一些特性(必须要掌握)
概念: C# 中的委托类似于 C/C++中的函数指针, 使用委托使程序员可以将方法引用封装在委托对象内。然后可以将该委托对象传递给可调用所引用方法的代码,而不必在编译时知道将调用哪个方法。与 C/C++中的函数指针不同,委托是面向对象、类型安全的,并且是安全的。
案例
using System;
class Program{
static void OtherClassMethod(){
Console.WriteLine("Delegate an other class's method");
}
static void Main(string[] args){
var test = new TestDelegate();
//deleagteMethod 搭载了三个函数
test.delegateMethod = new TestDelegate.DelegateMethod(test.NonStaticMethod);
test.delegateMethod += new TestDelegate.DelegateMethod(TestDelegate.StaticMethod);
test.delegateMethod += Program.OtherClassMethod;
test.RunDelegateMethods();
Console.ReadKey();
}
}
class TestDelegate{
public delegate void DelegateMethod(); //声明了一个Delegate Type
public DelegateMethod delegateMethod; //声明了一个Delegate对象
public static void StaticMethod(){
Console.WriteLine("Delegate a static method");
}
public void NonStaticMethod(){
Console.WriteLine("Delegate a non-static method");
}
public void RunDelegateMethods(){
if (delegateMethod != null){
Console.WriteLine("---------");
//运行被搭载在 delegateMethon 上的函数
delegateMethod.Invoke();
Console.WriteLine("---------");
}
}
}
输出如下
---------
Delegate a non-static method
Delegate a static method
Delegate an other class's method
---------
delegate 和 C++ 函数指针的区别?
delegate 使用场景
首先来看一个案例
class Car{
public delegate void Notify(int value);
public event Notify notifier;
private int petrol = 0;
public int Petrol{
//没有在这里直接调用 Alerter.Notify 是为了降低耦合性
get {
return petrol;
}
set{
petrol = value;
if (petrol < 10) //当petrol的值小于10时,出发警报
{
if (notifier != null){
notifier.Invoke(Petrol);
}
}
}
}
public Car(int petrol){
Petrol = petrol;
}
public void Run(int speed){
int distance = 0;
while (Petrol > 0){
Thread.Sleep(500);
Petrol--;
distance += speed;
Console.WriteLine("Car is running... Distance is " + distance.ToString());
}
}
}
class Alerter{
public Alerter(Car car){
car.notifier += new Car.Notify(NotEnoughPetrol);
}
public void NotEnoughPetrol(int value){
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("You only have " + value.ToString() + " gallon petrol left!");
Console.ResetColor();
}
}
using System;
using System.Threading;
class Program{
static void Main(string[] args){
var car = new Car(15);
new Alerter(car);
car.Run(120);
}
}
//输出以下内容
Car is running... Distance is 120
Car is running... Distance is 240
Car is running... Distance is 360
Car is running... Distance is 480
Car is running... Distance is 600
You only have 9 gallon petrol left!
Car is running... Distance is 720
You only have 8 gallon petrol left!
Car is running... Distance is 840
You only have 7 gallon petrol left!
Car is running... Distance is 960
You only have 6 gallon petrol left!
Car is running... Distance is 1080
You only have 5 gallon petrol left!
Car is running... Distance is 1200
You only have 4 gallon petrol left!
Car is running... Distance is 1320
You only have 3 gallon petrol left!
Car is running... Distance is 1440
You only have 2 gallon petrol left!
Car is running... Distance is 1560
You only have 1 gallon petrol left!
Car is running... Distance is 1680
You only have 0 gallon petrol left!
Car is running... Distance is 1800
总结: 其实以上的代码是设计模式中的观察者模式(观察者模式又称Source/Listener模式)的实现,当汽车在运行中汽油量<10 时,警报器便会发出警报。在上面代码中,Delegate 相当于一个存放回调函数的函数指针,使用Delegate,我们可以非常方便地实现观察者模式。而其实,在需要使用回调函数时,我们都可以考虑使用Delegate。
delegate 的使用场景
概要: 其实C#事件就是基于windows消息处理机制的,只是封装的更好,让开发者无须知道底层的消息处理机制,就可以开发出强大的基于事件的应用程序来。
采用事件编程的好处有哪些?
下面来简单剖析 C# event 事件
事件编程可以简单分为两个部分: 事件发生的类和事件接受处理的类
事件发生类说的就是: 在这个类中触发了一个事件,但这个类并不知道哪个对象或者方法会接收到并处理它触发的事件, 所需要的是在发送方和接收方之间存在一个媒介, 而这个媒介就是上面提到的 委托 ,
键盘按键 实例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1{
class EventArgs{
}
// 如果事件处理程序需要状态信息,就得派生一个类
// 因为我们这里是按键,所以需要记录下来,好让后面处理
internal class KeyEventArgs: EventArgs{
private char keyChar;
public KeyEventArgs(char keyChar):base(){
this.keyChar = keyChar;
}
public char KeyChar {
get {
return keyChar;
}
}
}
//再创建一个事件发生的类 KeyInputMonitor
//这个类用于监控键盘按键的输入并触发一个事件:
internal class KeyInputMonitor {
// 创建一个委托,返回类型为void,两个参数
public delegate void KeyDownHandler(object sender, KeyEventArgs e );
// 将创建的委托和事件关联起来
public event KeyDownHandler KeyDown;
public void Run() {
bool finished = false;
do {
Console.WriteLine("Input a char");
string response = Console.ReadLine();
char responseChar = (response == "") ? ' ' : char.ToUpper(response[0]);
switch (responseChar) {
case 'X':
finished = true;
break;
default:
// 得到按键信息的参数
KeyEventArgs keyEventArgs = new KeyEventArgs(responseChar);
// 触发事件
//并将事件交由KeyDownHandler这个委托来处理,委托指定事件处理方法去处理事件
//参数this是指触发事件的对象就是本身这个对象,keyEventArgs包含了按键信息。
KeyDown(this, keyEventArgs);
break;
}
} while (!finished);
}
}//end KeyInputMonitor
/*
* 最后创建一个事件接收方的类,这个类先产生一个委托实例,
* 再把这个委托实例添加到产生事件对象的事件列表中去,这个过程又叫订阅事件。
* 然后提供一个方法回显按键信息。*/
internal class EventReceiver {
public EventReceiver(KeyInputMonitor monitor) {
// 产生一个委托实例并添加到KeyInputMonitor产生的事件列表中
monitor.KeyDown += new KeyInputMonitor.KeyDownHandler(this.OnKeyDown);
}
private void OnKeyDown(object sender, KeyEventArgs e) {
// 真正的事件处理函数
Console.WriteLine("Capture key: {0}", e.KeyChar);
}
}
// 主函数调用
public class MainEntryPoint {
static void Main(String[] args) {
// 实例化一个事件发送器
KeyInputMonitor monitor = new KeyInputMonitor();
// 实例化一个事件接收器
EventReceiver eventReceiver = new EventReceiver(monitor);
// 运行
monitor.Run();
}
}
}
总结
C# 中使用事件的步骤
C# 中事件产生和实现的流程
public class A {
public delegate void EventHandler(object sender);
public event EventHandler a;
public void Run() {
Console.WriteLine("Trigger an event.");
a(this);
}
}
class B {
public B(A a) {
a.a += new A.EventHandler(this.b);
}
private void b(object sender) {
Console.WriteLine("Received and handled an event.");
Console.Read();
}
}
这里我们总结一下泛型的概念以及作用
概念: 泛型其实就是提供一个数据类型的抽象层,因为它泛所以抽象,方便了我们代码的重构和提取,我们无需hard-code接口中的数据类型,而是通过一个抽象泛型类型来指定数据类型,所以泛型可以提取出一个通用的接口。
其实这个概念在 C++ 中是有的, 很相似
使用泛型的直观好处
至于怎么使用C# 中的泛型编程, 由于篇幅太长
在这篇博客总结 : .NET 中的泛型(上篇)
dynamic 被编译后,实际是一个 object 类型,只不过编译器会对 dynamic 类型进行特殊处理,让它在编译期间不进行任何的类型检查,而是将类型检查放到了运行期
dynamic 类型转换:
//任何实例都能隐式转换为 dynamic 类型实例
dynamic d1 = 7;
dynamic d2 = "a string";
dynamic d3 = System.DateTime.Today;
dynamic d4 = System.Diagnostics.Process.GetProcesses();
//类型为dynamic的任何表达式也能够隐式转换为其他类型。
int i = d1;
string str = d2;
DateTime dt = d3;
System.Diagnostics.Process[] procs = d4;
dynamic 简化反射
案例:
public class DynamicSample{
public string Name {
get; set; }
public int Add(int a, int b){
return a + b;
}
}
反射
public class ProMain{
static void Main(string[] args) {
DynamicSample dynamicSample = new DynamicSample();
var addMethod = typeof(DynamicSample).GetMethod("Add");
int re = (int)addMethod.Invoke(dynamicSample, new object[] {
1, 2 });
Console.WriteLine("re:"+re);
Console.ReadKey();
}
}
进行简化
public class ProMain{
static void Main(string[] args) {
dynamic dynamicSample2 = new DynamicSample();
int re2 = dynamicSample2.Add(1, 2);
Console.WriteLine("re:"+re2);
Console.ReadKey();
}
}
使用场景
C# 参数的种类一共分为四种
下面主要讨论的是 按值传递和按引用传递的区别, 以及值类型和引用类型在按值传递和按引用传递时的表现
按值传递的参数
C # 的参数在默认情况下都是按值传递的。也就是说,当向方法传递参数的时候,会创建一个新的存储位置,然后将参数的值复制一份放到该存储位置中。相当于声明了一个局部变量(形参),然后用传入的参数的值初始化这个变量。如果在方法内改变形参的值,将不会影响到方法调用的上下文.
值类型按值传递: 复制该值类型本身所代表的数据
对于引用类型的实参按值传递的误区
例如下面的代码:
class Pro{
static void Main(String[] args) {
StringBuilder sb1 = new StringBuilder("Hello");
M(sb1);
Console.WriteLine(sb1);
}
static void M(StringBuilder sb2){
sb2 = null;
}
}
// 输出 hello
class Pro{
static void Main(String[] args) {
StringBuilder sb1 = new StringBuilder("Hello");
M(sb1);
Console.WriteLine(sb1);
}
static void M(StringBuilder sb2){
sb2.Append(" C#");
}
}
//输出 Hello C#
按引用传递的参数
按引用传递不会涉及隐式复制。它所传递的,不是在调用方法时传递给方法的变量的值,而是变量本身。它不会创建新的存储位置,而是使用与变量相同的存储位置,因此,在调用方法上下文中传递给方法的变量与方法内部使用的参数,实际上是同一个。
在按引用传递参数时,在方法的声明和调用的地方都必须显式使用 ref 修饰符,这是为了让你清楚你正在进行的是与默认传递方式不同的按引用传递。
值类型按引用传递:
class Pro{
static void Main(String[] args) {
int i = 5;
M(ref i);
Console.WriteLine(i);
}
static void M(ref int j) {
j = 10;
}
}
// 输出 10
引用类型按引用传递: 引用类型按引用传递与值类型按引用传递表现形式是一样的,在方法内部所做的任何改变,都将反映到外部变量上。
class Pro{
static void Main(String[] args) {
StringBuilder sb1 = new StringBuilder("Hello");
Console.WriteLine(sb1); //输出 Hello
M(ref sb1);
Console.WriteLine(sb1); //什么都不输出
}
static void M(ref StringBuilder sb2){
sb2 = null;
}
}