安装及第一个helloWorld
-
vscode安装C#拓展(可选)
-
新建helloWorld.cs文件
using System; namespace HelloApp{ class hello{ static void Main(string[] args){ Console.WriteLine("Hello world"); Console.ReadLine(); } } }注意tips:
- 每个表达式和语句后面都必须要有分号;
- 文件名可以不和类名一致
-
cmd中输入 csc helloWorld.cs ,目录下会出现helloWorld.exe运行即可看到cmd中显示‘ hello world'
在vscode中配置C#安装环境
-
扩展搜索并安装 C# 、C# Dev Kit
-
cmd输入dotnet new console dotnet build dotnet run 直到文件下方出现bin文件夹
- 之后每次修改文件后都要运行dotnet build,将修改的内容同步到dll文件中去 / 也可以直接用dotnet run调试 /或者修改后在解决方案资源管理器中重新生成
- 原因:js和c#不同,前者是可以直接在浏览器中运行的语言,后者是需要dotnet编译的语言,也可以理解为类似react/vue框架需要通过webpack编译成浏览器知道的语言才能运行
-
将Program文件的内容修改为上述所提到的helloWorld.cs
-
右侧debug按钮点击创建launch.json文件,可以看到出现.vscode文件夹里面有一个launch.json文件,为configurations中添加
{ "version": "0.2.0", "configurations": [ { "name":"C#net Launch", "type": "coreclr", "request": "launch", "program": "${workspaceFolder}/bin/Debug/net8.0/csharp.dll", //这里文件名可能不一致,根据你的文件名选择后缀为.dll的文件即可 } ] }随后只需按F5即可进行调式C#程序并在下方控制台看到输入的结果
- ctrl+shift+P选择打开NET:打开解决方案,在vscode文件栏下方可以看到出现了一个解决方案资源管理器,点击+即可进行添加class等操作
C#基本概念
using System;//必备,包含namespace之前
namespace xxxx{} //包含多个class的空间(主class、实例化class)
class xxx{} //类,包含属性、方法、Main函数
static void Main
double//各类标识符、关键字
tip: 关键字不能用作标识符,如果使用关键字作为标识符,可以在关键字前面加上 @ 字符作为前缀
数据类型
c#数据类型主要有:value\reference\pointer
-
value
- byte int -> float \ double \ decimal (浮点数从左到右逐渐精确,decimal需要额外存储空间)
- char' '(string为reference
- bool
- struct
-
reference: class、object 、 dynamic 和 string-》类同于js对象[栈中指针指向堆])
-
pointer:
char* cptr; int* iptr;
可空类型
可空类型(nullable):?用于对int\double\bool等无法直接赋值为null的数据类型进行null赋值
int? a=3; //变量a=3|null
?? 用于表示一个变量在为null的时候返回指定的值
int? a=null;
int b;
b=a ?? 5; //等同于b= (a==null)? 5 :null;
var声明的变量
在C#中用var声明的变量不需要指定数据类型,声明时必须初始化,且一旦声明后无法更改(与js中的var并不相同,倒是有点像js中的const)
var myName="liming";
常见操作value的方法
String
static void Main(string[] args){
stirng phrase="haha";
Console.WriteLine(phrase.Length); //获取string长度
Console.WriteLine(phrase[0]); //获取string下标对应的char
Console.WriteLine(phrase.IndexOf('a')); //获取char对应的index,这里是1,若找不到则返回-1
Console.WriteLine(phrase.Contains(h)); //字符串是否包含该char,return boolean
Console.WriteLine(phrase.Substring(0,2)); //截取字符串,左闭右开,这里是ha
Console.WriteLine(phrase.ToUpper()) //转化大小写ToLower()
Console.WriteLine("ch\nhaha"); //换行符
Console.WriteLine("Hello"World");//转义符
string value = "abc123";
char[] valueArray = value.ToCharArray();//string拆分为char,类似Join(),Split()
//连接字符串
Console.WriteLine($"hi {phrase}")
Console.WriteLine("hi"+phrase)
Console.ReadLine();
}
Number
static void Main(string[] args){
//1.可以直接使用数学运算、遵循运算规律
//int vs decimal ->decimal eg.5/2.0->2.5
//int vs int -> int eg.5/2 ->2 向下取整
int num=5;
num++;
num--;
//2.use Math.method()
Console.WriteLine(Math.Abs(num)); //other method like Math.Pow()\Math.max()...
Console.ReadLine();
}
数据类型的转换
强制转换
ReadLine()获取的为字符串
假设我们希望获取用户输入的两个数字,并返回他们相加的结果,实现这个程序的思路可能是分别用两个变量存储ReadLine()并相加,但由于ReadLine()获取到的变量默认是字符串,所有我们需要使用Convert方法,将变量转换为int或decimal:
//基本使用
int example=Convert.ToInt32("22") //22
//案例中使用
Console.Write("请输入第一个数字:");
double num1=Convert.ToDouble(Console.ReadLine());
Console.Write("请输入第二个数字:");
double num2=Convert.ToDouble(Console.ReadLine());
Console.WriteLine(num1+num2);
Console.ReadLine();
除了Convert,也可以使用另一种方式强制转换:
int num1=5;
int num2=2;
int val=num1/num2; //2
double res=(double)num1/(double)num2; //2.5
-
强制转换与Convert()区别
强制转换直接截断浮点数,Convert()会进行四舍五入
decimal num=1.5; int res=(int)num; //1 int result=Convert.ToInt(num); //2
还可以使用Pause() 、TryPause()方法:
- 将字符串转换为数字数据类型时,请使用
TryParse()。 - 如果转换成功,
TryParse()会返回true;如果失败,则会返回false。 - out 参数提供了返回值的方法的辅助手段。 在这种情况下,
out参数返回转换后的值
string first = "5";
string second = "7";
int sum = int.Parse(first) + int.Parse(second);
Console.WriteLine(sum);//12
string myInput="2.33455";
decimal.TryParse(myInput,out myInputDecimal);//myInputDecimal=2.33455
隐式转换
赋值给另外一个数or字符串+
byte b = 10;
int i = b; // 隐式转换,不需要显式转换
- 隐式转换只能将较小范围的数据类型转换为较大范围的数据类型,不能将较大范围的数据类型转换为较小范围的数据类型
数组
创建数组及修改数组的方法:
//创建数组
//直接给定
int[] nums={9,7,5,3,1};
//设定长度为3的字符串数组
string[] names=new string[3];
//重新赋值修改数组某一项
names[0]="linda";
创建及修改二维数组方法:
//创建二维数组[,] -> 直接给定
int[,] array={
{1,2},
{3,4},
{5,6}
}
//设定每一个元素有两个子元素,共有3个这样的元素
int[,] otherArray=new int[2,3]; //类似array
Console.WriteLine(array[0,1]) //2
foreach遍历数组:
foreach(int n in nums){
int sum+=n;
}
枚举Enum
枚举一系列数值,value类型,具体用法:
enum Day{Sun,Mon,Tue,Wed,Thu,Fri,Sat};
static void Main(){
int x=(int)Day.Sun;
int y=(int)Day.Tue;
Console.WriteLine("Sun={0}",x); //Sun=0
Console.WriteLine("Tue={0}",y); //Tue=3
}
结构体Struct
使用方式:
struct Songs{
public string songName;
public string author;
public int songId;
}
//in a class
Songs lover;
lover.title="lover";
lover.author="Taylor Swift";
lover.songId=1;
struct与class区别
- struct值类型,变量在赋值时会复制整个结构,每个变量都有自己的独立副本;class引用类型,两个变量指向同一个对象
- struct不能继承、多态,不能有无参数的构造函数;class支持继承多态,可以有无参数的构造函数
- struct不能直接设置为null(可以设置为可空类型):class实例默认可以为null
类、构造函数
我们在vscode中可以使用解决方案管理器来新建一个class,这里为Student,并为其添加属性、方法和构造函数
namespace cDemo;
public class Student
{
public string sName; //属性
public int sNumber; //属性
public void sayName(){ //方法
Console.WriteLine("my name is "+sName);
}
public Student(string name,int number ){//构造函数
sName=name;
sNumber=number;
}
}
在主class中就可以直接new Student类,也就是调用Student类中的构造函数,并使用其中的属性和方法:
namespace cDemo
{
class Program{
static void Main(string[] args){
Student liming=new ("liming",2020501); //构造函数
liming.sayName(); //my name is liming
Console.ReadLine();
}
}
}
public/private
get\set
上面演示的都是访问public修饰符属性的方法,如果我们想设置或访问private属性,需要用一个public属性并为他设置get和set方法,如下:
public class Student
{
public string sName;
public int sNumber;
private char sGpa; //sGpa是private属性
public char sRating //使用是sRating代替
{
get //外部调用Student.sRating得到Student.sGpa
{
return sGpa;
}
set //外部设置Student.sRating='A'
{
HashSet<char> grades = new HashSet<char> { 'A', 'B', 'C', 'D', 'E', 'F' };
if (grades.Contains(value))
{
sGpa = value;
}
else
{
sGpa = 'N';
}
}
}
}
如外部调用,如下:
Student liming = new("liming", 2020501);
liming.sRating = 'R';
Console.WriteLine(liming.sRating); //'N'
liming.sRating='C';
Console.WriteLine(liming.sRating); //'C'
其他修饰符
- public:所有对象都可以访问;
- internal:同一个namespace的对象可以访问;
- protected:只有该类对象及其子类对象可以访问
- protected internal:internal+protected
- private:对象本身在对象内部可以访问;
static
属性
在class内部具有static修饰符的属性只属于class本身而不属于实例,同样是Student:
调用class本身的属性可行,调用实例中的属性不可行,要想通过实例调用static属性,可以为其添加一个方法
//Student.cs
public class Student
{ public static string school="BJU";
public static int sCount=0;
}
//Program.cs
Student liming = new("liming", 2020501);
Student lihua=new("lihua",2020911);
Console.WriteLine("共有"+Student.sCount+"名学生"); //共有2名学生
Console.WriteLine("学生们都来自"+liming.school); //报错!
//访问static属性……
public class Student
{ public static string school="BJU";
public static int sCount=0;
public int getStudentCount(){ //为他添加一个方法
return sCount;
}
}
//Program.cs
Student liming = new("liming", 2020501);
Student lihua=new("lihua",2020911);
Console.WriteLine("学生们都来自"+liming.getStudentCount()); //学生们都来自BJU
方法/class
我们也可以为方法和class添加static修饰符,一般使用这种形式来当作工作函数使用:
//UsefulTool.cs
public static class UsefulTool{
public static void test(string word){
Console.WriteLine(word);
}
}
//Program.cs
//调用:
UsefulTool.test("hi");
//类似诸如此类的工具函数:
Math.Max(2,6);
继承
我们可以通过继承一个类的属性和方法,并在此基础上进行拓展和重写:
假设我们希望SuperStudent继承Student类,我们可以:
public class SuperStudent:Student{
//在这里补充新的属性和方法
}
如果需要重写一个方法:
-
在继承的母类方法上添加virtual
public virtual void sayName() { Console.WriteLine("my name is " + sName); } -
在子类方法上添加override
public override void sayName() { Console.WriteLine("i am super student,my name is " + sName); }
接口Interface
和类的用法一致
C#方法
C#的Method类似其他编程语言中的Function,在C#中只有Main方法中的代码会被执行,我们可以通过复用Function/Method达到简化Main函数当中的目的。一般而言,方法名使用大写字母开头,如下示范Method的使用:
using System;
namespace Program{
class myProgram{
static void Main(string[] args){
Console.Write("请输入你的姓名:");
string name;
name=Console.ReadLine();
SayHi(name); //调用SayHi方法
Console.ReadLine();
static void SayHi(string userName){
Console.WriteLine("hello!"+userName);
}
}
}
可以看到上面我们使用的是void关键字,这个关键字代表函数不会返回任何值,如果我们的函数将要返回一个值,我们可以用return关键字来表达,下面是一个示范:
class myProgram{
static void Main(string[] args){
Console.Write("请输入数字:");
int num=Convert.ToInt32(Console.ReadLine()); //需要使用convert转换为数字,不能直接ToInt,而是ToInt32
int cubeNum=cube(num);
Console.WriteLine(cubeNum);
Console.ReadLine();
}
static int cube(int num){
return num*num; //返回该数字的平方
}
}
语句
c#中的if\switch\while\do while语句用法与js中相同,可以使用&&、||
for循环中注意声明变量不是使用let/var,而是根据变量的数据类型,以下是一个常见的打印数组各类数据项的for循环:
static void Main(string[] args){
int[] nums={1,9,7,5,3}
for(int i=0;i<nums.Length;i++){ //c#的length要大写
Console.WriteLine(nums[i])
}
Console.ReadLine();
}
C#调试与错误捕获
- 错误捕获
在C#中也可以使用try...catch进行错误捕获,其中catch可以根据Exception对象的不同catch多次,也可以使用finally关键字
try{
//需要执行的代码
}
catch(Exception e){ //也可以使用其他更为细节的exception
Console.WriteLine(e.Message);//打印错误信息
}
catch(FormatException e){//格式错误
Console.WriteLine(e.Message);
}
finally{
//最终总是会执行always execute
Console.ReadLine();
}
-
调试
预处理器#,在编译开始之前对信息进行预处理,如果计划发布两个版本的代码,即基本版本和有更多功能的企业版本,就可以使用这些预处理器指令来控制。一般使用#define 与其他处理器配合使用。
#define Test //测试版本 using System; namespace TestApp{ class Program{ static void Main(string[] args){ #if (Tset) .....//进行操作 #error //生产错误 #warning //生成警告 } } }