摘要
考虑到mfc的学习已经稍微达到中级的入门,现阶段需要学习UE,而UE很多涉及C#知识。故开始C#的学习。之所以要换一门语言是考虑到MFC做前端属实事半功倍。学习另一门框架、语言,如果与自己已熟悉的知识体系映射起来,会极大提高学习效率。本文对二者的差异进行总结
正文
一、编译相关
MFC的编译
就是C、C++语言的 预处理→编译→ 汇编→ 链接
生成的.dll .exe 文件 直接运行就可以了,资源文件(图标 数据)大多在这些文件里直接嵌套进去了。
C# .NET的编译
第一步 把C# 编译成 通用中间语言 (Common Intermediate Language,CIL) 生成的文件存在程序集中,这个程序集包含了熟悉的.exe文件、资源文件(图标、数据、序列化文件之类)也称元数据。
第二步 把 通用中间语言代码编译成 本机代码(Just-In-Time编译器完成)
GAC (Global Assembly Cache)全局程序集 缓存
二、运行相关
c++程序的运行
走的msvcr90.dll 之类的
c#程序的运行
走的是CLR公共语言运行时。
三、权限相关
c++程序的权限
直接访问系统API
c#程序的运行
托管环境所支持的。
内存管理就“基本”不需要程序员了。
四、变量表达与运算相关
C#胜于C++的特性
字符串字面值,“@”
对带有转义的字符串,之前:“C:\\Doc\\加油站.torrent”
使用“@”后:@“C:\Doc\加油站.torrent”
运算符优先级
C# 与 C++保持一致
优先级 | 运算符 |
---|---|
优先级由高到低 | ++、 --(作前缀)、 +、 -、 |
* 、/、 % | |
+ 、- | |
=、*=、/=、%=、+=、-= | |
++、-- 作后缀 |
闲杂
string strName = "Baotong";
strName = strName.PadLeft(10, '-');
这里参数写的是10 但是在左边添加了三个‘-’
字符,是因为10是总长度
五、条件分支相关
Switch语句
- C++ case可以执行多个,而在C#中则属于异常!
- C# 骚操作:
int key = 10;
switch(key)
{
case int value when value < 10: //如果是正数则进入该分支
break;
case int value when value > 10:
break;
case var var1:// 变量类型
Console.WriteLine(var1.GetType().Name);
break;
}
六、循环相关
foreach语句
循环体里只能进行读,不能写
七、类型转换相关
C# 有个新特性 checked()函数,可以检测是否在类型转换时存在溢出!
string类型的ToDouble ToInt方法都会对溢出进行检查
八、函数相关
参数
- C# 支持函数参数 为 “参数数组”,这个特殊参数只能是最后一个参数,且需用关键字
params
定义。
static int SumVars(params int[] vars)
{
int sum = 0;
foreach(int var in vars)
{
sum += var;
}
return sum;
}
static void Main()
{
int nSum = SumVars(1, 2, 3, 4, 5, 6, 7);
}
- C#的
ref、out
关键字等价于 C++ 的引用修饰&
,有一区别是:C#传递实参的时候也必须加上这个关键字,且ref
传参前必须初始化,out
使用时必须当做未初始化的值。 - C# 支持return多个参数,方法是使用元组
static (int max , int min , double average) GetMaxMin(IEnumerable<int> num)
{
return (Enumerable.Max(num), Enumerable.Min(num), Enumerable.Average(num));
}
static void Main()
{
IEnumerable<int> numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var result = GetMaxMin(numbers);
}
//Enumerable 用于查询的类 , IEnumerable 现在当做泛型参数理解
- 委托,对应c++的延迟绑定,就是让运行时决定 调用那个函数来执行。
class Program
{
delegate double ProcessDelegate(double param1, double param2);//1.定义
static double Multiply(double param1, double param2) => param1 * param2;
static double Divide(double param1, double param2) => param2 / param2;
static void Main(string[] args)
{
ProcessDelegate process;
Console.WriteLine("Enter 2 numbers separated with a comma:");
string input = Console.ReadLine();
int commaPos = input.IndexOf(',');
double param1 = Convert.ToDouble(input.Substring(0,commaPos));
double param2 = Convert.ToDouble(input.Substring(commaPos+1,input.Length - commaPos -1));
Console.WriteLine("Enter M to multiply or D to divide:");
input = Console.ReadLine();
if (input == "M")
process = new ProcessDelegate(Multiply);//2.绑定
else
process = new ProcessDelegate(Divide);
Console.WriteLine($"Result:{process(param1, param2)}");//3.调用
Console.ReadKey();
Console.ReadKey();
}
}
九、面向对象相关
Object类
可以把C#中Object 粗粗的理解为 C里面的指针,32位系统下指针的大小都是四个字节,不管是啥对象的指针。异曲同工,C#中Object是所有对象的基类,也就是可以用Object接收所有类。
继承类 接口实现的语法
C#写法
public class MyClass : MyBase,IMyInterface
{
//首字母 I,一般表示该类为接口
}
接口的定义
public IMyInterface
{//接口成员
}
多态性的使用
//使用Object基类的GetType()方法,获取System.Type 类型的返回值
if(myObj.GetType() == typeof(类名(例如汽车类)))
{
//加汽油
}
VS编译器图标含义
C# 接口和抽象类的相同点与不同点
1、成员可以由派生类继承(相同)
2、不能直接实例化(相同)
3、派生类只能继承自一个基类,可以用继承链包含多个抽象类,而一个类可以使用多个接口。
4、抽象类可以拥有抽象成员和非抽象成员,而接口成员必须都在使用接口的类上实现。接口成员是公共的,但抽象类的成员各种各样。
5、接口不包含字段、构造函数、析构函数、静态成员或常量。
抽象类用作基类,不同继承类有共性的部分。
接口则主要用于类,各个实现有根本的区别。
C#中结构是值类型,对象是引用类型
也就是说结构的复制是做的拷贝,而对象的复制传递的是指针。
C# 在vs编译器中快速创建属性
输入prop 快速按两下TAB键。
C# 支持隐藏基类的成员
C++如果子类重写了父类的方法,则通过基类指针调用该方法时也会执行子类的方法。这一点与C# “override”修饰的方法一致。C#若没这个关键字则基类指针还是执行的基类的方法。
C#强制调用基类的方法
使用base关键字,base.DoMethod();
C# 支持一个类 分不同的文件定义
!!!
C# 部分方法相关
部分方法就是一个文件写申明,一个文件写实现,类似.h和.cpp那味。
C# 部分方法不能有返回值,任何参数不能是out修饰。编译时如果没有该方法没有实现则编译器直接把该方法抹去。
为啥部分方法不能有返回值?
因为如果有返回值则可能有一个变量用来接收这个方法的返回值,编译器直接抹去这个值是不安全的。接收返回值的这个变量可能就成了随机数,有极大隐患。out修饰的参数也是类似的道理。
C# 封箱和拆箱
封箱是把值类型转换为System.Object类型,或者转换为由值类型实现的接口类型。(子类 -> 父类)
拆箱是相反的过程。
十 、比较相关
C++ 与C# 都支持运算符(大于、小于、等于…)重载。此外C#有两个接口,分别是IComparable和IComparer。
IComparable:提供CompareTo()方法
IComparer:提供Compare()方法
//**下面这段代码是Class Person继承了IComparable接口,有啥用呢?
//*就是当我们把Class Person存储到list、queue等容器后,调用sort方法,则会按这个CompareTo规则进行排序
class Person : IComparable
{
public string Name;
public int Age;
public Person(string name, int age)
{
Name = name;
Age = age;
}
public int CompareTo(object obj)
{
if (obj is Person)
{
Person otherPerson = obj as Person;
return this.Age - otherPerson.Age;
}
else
{
throw new ArgumentException(
"Object to compare to is not a Person object.");
}
}
}
//**下面这段代码是Class Person继承了IComparer接口,与上面有啥区别?
//**Comparer 翻译过来是比较器的意思,也就是这个类就是做比较用的,比如可以在Compare方法中接收各种各样的类,判断类型进行比较输出即可。而上面继承IComparable则只能和自身的类比较。
public class PersonComparerName : IComparer
{
public static IComparer Default = new PersonComparerName();
public int Compare(object x, object y)
{
if (x is Person && y is Person)
{
return Comparer.Default.Compare(
((Person)x).Name, ((Person)y).Name);
}
else
{
throw new ArgumentException(
"One or both objects to compare are not Person objects.");
}
}
}
十一、泛型相关
形式上换汤不换药,C# 的泛型也是尖括号指定类型。但实质上C# 的泛型的类型判断都是在运行时,而C++是编译期间确定的。有句总结很精辟:“泛型不止涉及集合,但集合特别适合泛型”。泛型的好处是简化代码,坏处就是代码可读性差。
十二 Lambda表达式
Lambda表达式的学习,抓住其优势就行了。使用lambda表达式无非就是为了把一些很小的代码写成匿名函数,增强可读性。从写的角度来说,写不出来也无所谓,按照平常的子函数写法就行了。从读的角度来说,实在读不懂就先拆成子函数!