笔者由于公司需要从 Java 转为 C# 所以翻阅网上资料学习撰写了本篇文档帮助拥有相同状况的人快速熟悉 C# 开发。由于笔者水平有限,如有错误请多包涵。
一个 C# 程序主要包括
并且 C# 文件的后缀为 .cs
C# 和 Java 的程序结构十分相似,Java 的结构是(项目 —— 包 —— 类),而 C# 是(解决方案 —— 命名空间 —— 类)。
using System;
namespace HelloWorldApplication
{
class HelloWorld
{
static void Main(string[] args)
{
/* 我的第一个 C# 程序*/
Console.WriteLine("Hello World");
Console.ReadKey();
}
}
}
这是 C# 中的 HelloWorld 程序
程序的第一行 using System; using 关键字用于在程序中包含 System 命名空间,一个程序可以拥有多个 using 语句,相当于 Java 中的 import 关键字进行导包。
namespace 关键字代表的是命名空间,一个命名空间可以拥有多个类。相当于 Java 的 package 关键字,用于区分类
class 关键字,用于声明类变量。类一般包含多个方法和属性。基本与 Java 无异。
Main 方法,是所有 C# 程序的 入口点。Main 方法说明当执行时 类将做什么动作。
Main 方法通过语句 Console.WriteLine(“Hello World”); 指定了它的行为。
WriteLine 是一个定义在 System 命名空间中的 Console 类的一个方法。该语句会在屏幕上显示消息 “Hello World”,类似 java 中的 sout
Console.ReadLine() 可以用来接收用户的输入。
注意:
在 C# 中标识符必须遵循以下规则:
在 C# 中,变量分为以下几种类型:
C# 中的值类型对比 Java 多出不少。
与 Java 相同 C# 中的object 类型是所有类的超类。
当一个值类型转换为对象类型时,则被称为 装箱;另一方面,当一个对象类型转换为值类型时,则被称为 拆箱。
dynamic 类型
dynamic 可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的。
dynamic d = 20;
字符串(String)类型
String 允许给变量分配任何字符串值。字符串(String)类型是 System.String 类的别名。它是从对象(Object)类型派生的。与 Java 不同的是字符串(String)类型的值可以通过两种形式进行分配:引号和 @引号。
String str = "runoob.com";
@
@"runoob.com";
C# string 字符串的前面可以加 @(称作"逐字字符串")将转义字符(\)当作普通字符对待,比如:
string str = @"C:\Windows";
//等价于
string str = "C:\\Windows";
@ 字符串中可以任意换行,换行符及缩进空格都计算在字符串长度之内。
string str = @"";
指针类型变量存储另一种类型的内存地址。C# 中的指针与 C 或 C++ 中的指针有相同的功能。
声明指针类型的语法:
char* cptr;
int* iptr;
在 C# 中使用 const 关键字来定义常量
public const int c1 = 5;
不可继承类由 sealed 进行修饰
访问修饰符 | 范围 |
---|---|
public | 所有对象(与 Java 一致) |
private | 对象本身(与 Java 一致) |
protected | 只有该类对象及其子类 |
internal | 同一个程序集的对象 |
protected internal | 当前程序集或派生类(与 Java 中的 protected 一致) |
在 C# 中类默认是使用 internal,而成员的默认访问修饰符是 private。
方式 | 描述 |
---|---|
值参数 | 这种方式复制参数的实际值给函数的形式参数,实参和形参使用的是两个不同内存中的值。在这种情况下,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。 |
引用参数 | 这种方式复制参数的内存位置的引用给形式参数。这意味着,当形参的值发生改变时,同时也改变实参的值。 |
输出参数 | 这种方式可以返回多个值。 |
值参数与Java 相同。
但是引用传递和输出参数与 Java 不同。
在 C# 中使用 ref 关键字声明引用参数,引用参数会提供给方法的实际参数具有相同的内存位置。
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public void swap(ref int x, ref int y)
{
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 temp 赋值给 y */
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
/* 局部变量定义 */
int a = 100;
int b = 200;
Console.WriteLine("在交换之前,a 的值: {0}", a);
Console.WriteLine("在交换之前,b 的值: {0}", b);
/* 调用函数来交换值 */
n.swap(ref a, ref b);
Console.WriteLine("在交换之后,a 的值: {0}", a);
Console.WriteLine("在交换之后,b 的值: {0}", b);
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
在交换之前,a 的值:100
在交换之前,b 的值:200
在交换之后,a 的值:200
在交换之后,b 的值:100
按输出传递参数
return 语句可用于只从函数中返回一个值。但是可以使用 out 关键字输出参数来从函数中返回两个值。输出参数会把方法输出的数据赋给自己,其他方面与引用参数相似。当需要从一个参数没有指定初始值的方法中返回值时,输出参数特别有用,如下。
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public void getValues(out int x, out int y )
{
Console.WriteLine("请输入第一个值: ");
x = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("请输入第二个值: ");
y = Convert.ToInt32(Console.ReadLine());
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
/* 局部变量定义 */
int a , b;
/* 调用函数来获取值 */
n.getValues(out a, out b);
Console.WriteLine("在方法调用之后,a 的值: {0}", a);
Console.WriteLine("在方法调用之后,b 的值: {0}", b);
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果(取决于用户输入):
请输入第一个值:
7
请输入第二个值:
8
在方法调用之后,a 的值: 7
在方法调用之后,b 的值: 8
在 C# 中除了基本数据类型外,还拥有 ? 可空类型(Nullable),它可以使基本数据类型等无法为 null 的数据类型进行 null 的赋值
int i; //默认值0
int? ii; //默认值null
?? 问号用于判断一个变量在为 null 的时候返回一个指定的值
如果第一个操作数的值为 null,则运算符返回第二个操作数的值,否则返回第一个操作数的值。下面的实例演示了这点:
using System;
namespace CalculatorApplication
{
class NullablesAtShow
{
static void Main(string[] args)
{
double? num1 = null;
double? num2 = 3.14157;
double num3;
num3 = num1 ?? 5.34; // num1 如果为空值则返回 5.34
Console.WriteLine("num3 的值: {0}", num3);
num3 = num2 ?? 5.34;
Console.WriteLine("num3 的值: {0}", num3);
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
num3 的值: 5.34
num3 的值: 3.14157
在 C# 中使用 ( : ) 冒号表示继承和实现,在书写规范上 C# 推荐接口用 I 开头来区别类和接口的不同。java中继承使用 extends 关键字,实现使用:implements。
java里子类调用父类方法或者实现父类构造方法使用 super 关键字,而在 C# 里使用 base 关键字
接下来是与 Java 区别较大的地方
动态多态性是通过 抽象类 和 虚方法 实现的。
在 C# 中方法的重写由 虚方法 来完成
虚方法是使用关键字 virtual 声明的(被继承的方法)。重写的方法用 override 进行修饰(继承的方法)。
虚方法可以在不同的继承类中有不同的实现。
对虚方法的调用是在运行时发生的。
抽象类中可以声明抽象属性,由实现类实现。不知道有啥用?
public abstract string Name
{
get;
set;
}
C# 中定义结构必须使用 struct 语句,且在方法体内声明的字段无法赋初值。
struct Books
{
public string title;
public string author;
public string subject;
public int book_id;
};
类的 析构函数 是类的一个特殊的成员函数,当类的对象超出范围时执行。
析构函数的名称是在类的名称前加上一个波浪形(~)作为前缀,它不返回值,也不带任何参数。
析构函数用于在结束程序(比如关闭文件、释放内存等)之前释放资源。析构函数不能继承或重载。
在 C# 中可以重定义或重载运算符。因此,程序员也可以使用用户自定义类型的运算符。重载运算符是具有特殊名称的函数,是通过关键字 operator 后跟运算符的符号来定义的。与其他函数一样,重载运算符有返回类型和参数列表。
public static Box operator+ (Box b, Box c)
{
Box box = new Box();
box.length = b.length + c.length;
box.breadth = b.breadth + c.breadth;
box.height = b.height + c.height;
return box;
}
上面的函数为用户自定义的类 Box 实现了加法运算符(+)。它把两个 Box 对象的属性相加,并返回相加后的 Box 对象。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LaIGOLfg-1660651041918)(C:\Users\墨\AppData\Roaming\Typora\typora-user-images\image-20220816193010203.png)]
预处理器指令指导编译器在实际编译开始之前对信息进行预处理。
所有的预处理器指令都是以 # 开始。且在一行上,只有空白字符可以出现在预处理器指令之前。预处理器指令不是语句,所以它们不以分号(;)结束。
C# 编译器没有一个单独的预处理器,但是,指令被处理时就像是有一个单独的预处理器一样。在 C# 中,预处理器指令用于在条件编译中起作用。与 C 和 C++ 不同的是,它们不是用来创建宏。一个预处理器指令必须是该行上的唯一指令。