C# 作为一门高级编程语言,其实他和 Java 非常类似,如果有学习过 Java 语言的小伙伴,应该会对 C# 特别熟悉。如果你对 C# 语言不熟悉也没有关系,本文将会从头开始带你熟悉 C# 的知识点。
在学习之前,我们要明白:
- 每天学习做好笔记,思维导图。不是为了以后更好复习,而是为了能够更好的梳理以及记忆知识点。
- 万丈高楼平地起,要想学习好,必须打好基础,切记心浮气躁
简介:.Net 平台 .Netframework 框架提供了运行环境和技术
了解 C#
C# 是一门面向对象的高级编程语言, 那相对应的。还有面向过程的语言,汇编语言等等。。。这些可以自己后续去了解。
面向对象编程按照现实世界的特点来管理复杂的事物,把它们抽象为对象,具有自己的状态和行为,通过对消息的反应来完成任务。这种编程方法提供了非常强大的多样性,大大增加了代码的重用机会,增加了程序开发的速度,将具备独立性特制的程序代码包装起来,修改部分程序代码时不至于会影响到程序的其他部分。
面向对象(OOP)具有三大特征:
- 封装(Encapsulation):只暴露必要的接口供外部访问,将类内部一些方法和属性私有化。隐藏具体实现以及细节。封装优点:隐藏信息,保护数据。降低耦合。
- 继承(Inheritance):继承可以实现现有类的所有功能,在无须编写原有代码的情况下,可以直接拓展新功能。继承优点:实现代码重用
- 多态(Polymorphism):多态是面向对象编程的一个重要概念,它允许不同类的对象对相同的消息做出响应,表现出不同的行为。多态通过方法的重写和基类引用指向派生类对象来实现。
.net 可以干什么?
在学习一门语言之前,我们是不是都会好奇这门语言可以做些什么。看看是不是和预期目标一致。
- 桌面应用程序 winform
- Internet 应用程序 ASP.NET 比如:携程、招商银行 APP
- 手机应用程序开发
- Unity 游戏开发或者虚拟现实
- 工控上位机开发,运动控制以及视觉检测
ORM 框架:对象关系映射
SqlSugar (国内)
Dos.ORM (国内)
Chloe (国内)
StackExchange/Dapper (国外)
Entity Framework (EF) (国外) EFCore 连接数据库
NHibernate (国外)
ServiceStack/ServiceStack.OrmLite (国外)
linq2db (国外)
Massive (国外)
PetaPoco (国外)
安装 Visual Studio 2022
此时我们对 C# 有了一个简单的了解,那我们可以观看视频链接,学习一下如何安装 C# 的编译器吧。2024 最新 C# 零基础入门教程 | Visual Studio 2022 入门
启动 Visual Studio:
- win + R 调出 cmd 窗口 输入 devenv
.sln 文件:
- 解决方案文件,包含着整个解决方案的信息,可以双击运行
.csproj 文件:
- 项目文件,里面包着着整个项目的信息,可以双击运行
注:代码所有标点都是英文半角
Visual Studio 快捷键
介绍了常用的 Visual Studio 快捷键,在需要使用的时候可以查看。不需要死记硬背,多用即可。
启动:F5
生成解决方案(查看错误代码):crtl + shitf + b
快速对齐代码:crtl + k + D
智能补全代码:Ctrl + J
复制鼠标所在行:crtl + D
提取方法: alt + shift + 10
多行注释:crtl + shift + /
注释所选代码 (使用的是单行注释):ctrl + k + C
取消注释代码:Ctrl + K + U
返回定义代码:ctrl + -
选中行首和行尾:shift + home
选中行尾:shift + end
(上下键移动可以选择多行)
折叠冗余代码,方便代码管理:#region
(空格 说明区域内代码的作用) #endregion
一起使用
打开开发文档:F1
转小写:Ctrl+ U
转大写:CTRL + SHIFT + U
返回撤销:ctrl + Y
拆分窗口:Alt + w
里面选择拆分(能将一个代码窗口拆成两部分,方便对比)
C# 第一个程序 Hello World
现在我们已经安装好了 Visual Studio 2022,那是不是已经迫不及待开始写程序了。那首先还是老套路,程序员的第一门课。“hello world”
一个 C# 程序主要包括以下部分:
- 命名空间声明(Namespace declaration)
- 一个 class 类
- Class 方法
- Class 属性
- 一个 Main 方法
- 语句(Statements)& 表达式(Expressions)
- 注释
C# 文件的后缀为 .cs
。
using System; //命名空间 | |
namespace HelloWorldApplication //项目名称 | |
{ | |
class HelloWorld | |
{ | |
static void Main(string[] args) | |
{ | |
/* 我的第一个 C# 程序*/ | |
Console.WriteLine("Hello World"); | |
Console.ReadKey(); /* 暂停程序,等待一个按键操作,按下的任意键将显示在控制台当中(下面没有暂停就是一闪而过) */ | |
string in = Console.ReadLine() //接受用户输入 一般写string类型接受值,但是有需求可以转换 | |
} //convert.Int16(parm); | |
} | |
} |
以下几点值得注意:
- C# 是大小写敏感的。
- 所有的语句和表达式必须以英文半角分号
;
结尾。 - 程序的执行从 Main 方法开始。
- 与 Java 不同的是,文件名可以不同于类的名称。
一开始我们都是使用控制台去输出结果。例如:hello world
如果在 DOS 控制台出现乱码现象。我们可以使用如下代码修改编码格式。
设置 DOS 窗口代码页(Code Page):
设置当前代码页为 UTF-8:chcp 65001
设置当前代码页为 GBK:chcp 936
CLR (Common Language RunTime, 公共语言运行库)
如果在项目中使用了几个项目,但是想运行特定项目。需要去设置启动项目。
启动项目设置:右键点击解决方案,在属性窗口中。不用的项目可以卸载,需要使用的时候重新加载
注释
文档注释:/// 三斜杠 多用来注释类或者方法 (java 的文档注释: // 内容 // )
多行注释: /* 注释内容 */
变量
using System; | |
namespace CycleDemo | |
{ | |
const double P = 3.14d; | |
class Cycle | |
{ | |
// 成员变量 | |
public double radius; | |
public double GetPerimeter() | |
{ | |
return 2 * P * radius; | |
} | |
public void Display() | |
{ | |
Console.WriteLine("radius: {0}", radius); | |
Console.WriteLine("Perimeter: {0}", GetPerimeter()); | |
} | |
} | |
class Execute | |
{ | |
static void Main(string[] args) | |
{ | |
Cycle c = new Cycle(); | |
c.radius = double.Parse(Console.ReadLine()); | |
c.Display(); | |
Console.ReadKey(); | |
} | |
} | |
} |
变量必须遵从:先声明 再赋值 最后使用
java 标识符 是以 下划线、字母、$ 开头
c# 标识符 是以 下划线、字母、@开头
camel 驼峰命名原则:要求变量名首字母单词小写,其余单词首字母大写
Pascal :每一个单词首字母都是大写 用于类名和方法名
局部变量是在函数、复合语句内、for 循环中。存储在栈区;
全局变量是在函数外部定义的变量;( C# 没有此概念,是用静态变量去实现 “全局变量” 的效果 )
成员变量在方法外部,定义在类中,在整个类中可以被访问,成员变量随着对象的建立消失而定,存在于堆内存当中,有初始值
var 特点
- 必须在定义时初始化。也就是必须是 var s = “abcd” 形式,而不能是如下形式: var s; s = “abcd”;
- 一但初始化完成,就不能再给变量赋与初始化值类型不同的值了。 var s = “hello” 是字符串,以后不能赋值为 int 类型
- var 要求是局部变量。
// 在局部变量中(即在方法体中)可以使用 var 来代替具体数据类型来定义变量
var temp = "C#"; |
静态和动态常量
C# 静态常量是使用 const 关键字来定义的 。定义一个静态常量的语法如下:
const <data_type> <constant_name> = value; | |
//静态常量编译时,使用常量的值去替换 | |
int value = <constant_name> | |
//是使用<constant_name>的值去替换value |
静态常量必须是编译时常量,而编译时常量必须是以下类型之一:
- 值类型(value types):比如整数、浮点数、字符和布尔类型。
- 字符串类型。
- 枚举类型。
- Null 类型。
C# 动态常量是使用 readonly 关键字来定义的 。定义一个动态常量的语法如下:
readonly <data_type> <constant_name> = value; |
readonly 以引用方式进行工作,某个常量更新后,所有引用该常量的地方均能得到更新后的值。
动态常量赋值后不可改变:
- 值类型,值类型的值不能改变
- 引用类型,引用地址不能改变。但是地址里面的值可以修改
静态常量和动态常量两者的区别如下:
Java 静态常量是使用 static final
public static final <data_Type> <constant_name> = value; |
堆栈和静态存储区
堆:空间比较大,但是读取速度比较慢 ps: 实例化出来的对象放在堆空间中
能在任意区域插入和删除
栈:空间比较小,但是读取速度比较快 ps: 局部变量放在栈中
数据只能在栈顶部进行插入和删除,先进后出
交换变量(面试题)
当面试说不可以使用第三个变量来交换变量时
int n1,n2; | |
n1 = n1 - n2; | |
n2 = n1 + n2; | |
n1 = n2 - n1; |
数据类型
值类型、引用类型、指针类型。目前这里只对值类型和引用类型进行介绍,指针类型不做过多赘述
值类型
byte,short,int,long,float,double,decimal,char,bool 和 struct 统称为值类型。
类型 | 描述 | 范围 |
---|---|---|
decimal | 128 位精确的十进制值,28-29 有效位数 | (-7.9 x 1028 到 7.9 x 1028) / 100 到 28 |
sbyte | 8 位有符号整数类型 | -128 到 127 |
short | 16 位有符号整数类型 | -32,768 到 32,767 |
uint | 32 位无符号整数类型 | 0 到 4,294,967,295 |
ulong | 64 位无符号整数类型 | 0 到 18,446,744,073,709,551,615 |
ushort | 16 位无符号整数类型 | 0 到 65,535 |
var | 它是一个声明变量的占位符。它主要用于在声明变量时,无法确定数据类型时使用。 |
float、double、long 类型变量赋值添加 F、D、L(大小写均可) 尾缀。默认为 double 类型,float 不加 f 报错。
double 类型,声明整数为 double 类型时,需要加后缀 D 或 d
decimal 后缀必须加 M 或 m,不加则视为 double 类型
三种数据类型转换
(int)值类型数据转换 显示类型强制转换
parse 数据类型转换,一般用于字符串转换成值类型
Convert 万能转换器。如果值是小数
- 小数等于 5,奇进偶不进。例如:87.5 = 88 88.5 = 88
两种数据类型的的区别:
- 基本类型的变量保存原始值,即它代表的值就是数值本身;
- 值类型变量传递时,将自己复制一份
- 引用类型变量传递时,将自己的内存地址赋值给新变量
引用类型
引用类型不包含存储在变量中的实际数据,但它们包含对变量的引用。
换句话说,它们指的是一个内存位置。使用多个变量时,引用类型可以指向一个内存位置。如果内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。内置的 引用类型有:object、dynamic 和 string。
1. 对象(Object)类型
对象(Object)类型 是 C# 通用类型系统(Common Type System – CTS)中所有数据类型的终极基类。Object 是 System.Object 类的别名。所以对象(Object)类型可以被分配任何其他类型(值类型、引用类型、预定义类型或用户自定义类型)的值。但是,在分配值之前,需要先进行类型转换。
当一个值类型转换为对象类型时,则被称为 装箱;另一方面,当一个对象类型转换为值类型时,则被称为 拆箱。
2. 动态(dynamic)类型
可以在动态类型的变量中存储任何类型的值。动态类型与对象类型相似,但是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的。
3. 字符串(String)类型
字符串(String)类型 允许您给变量分配任何字符串值。
值参数、引用参数(ref)、输出参数 (out)
形参:声明在方法的参数列表中,方法传的参数 public void Send (string message) ==> 其中 message 为形参
实参:初始化形参的变量或表达式。 string s = “Hello World”; Send (s);//s 为实参
方式 | 描述 |
---|---|
值参数 | 这种方式复制参数的实际值给函数的形式参数,实参和形参使用的是两个不同内存中的值。在这种情况下,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。 |
引用参数 | 这种方式复制参数的内存位置的引用给形式参数。这意味着,当形参的值发生改变时,同时也改变实参的值。// 使用 ref 关键字声明引用参数 /* 调用函数来交换值 */ n.swap(ref a, ref b); |
输出参数 | 这种方式可以返回多个值。 //n.getValue (out a); 使用 out 关键字声明引用参数 |
out 参数:返回值多个,不限类型。注意事项:
- 调用方法之前,对 out 参数传递的变量只需声明,可以赋值也可以不赋值,赋值也会在方法中被覆盖掉
- 使用 out 参数传递变量时,必须在方法内为其赋值,否则 return 的返回值没有意义
- 方法的参数使用 out 修饰时,调用该方法时也要加上 out 关键字
- 使用 out 修饰的变量不需要 return
static void Main(string[] args) | |
{ | |
int[] nums = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; | |
//使用out传值,不需要初始化 | |
int max; | |
int min; | |
int sum; | |
int avg; | |
string s; | |
Test(nums, out max, out min, out sum, out avg, out s); | |
Console.WriteLine(max); | |
Console.WriteLine(min); | |
Console.WriteLine(sum); | |
Console.WriteLine(avg); | |
Console.WriteLine(s); | |
Console.ReadKey(); | |
} | |
public static void Test(int[] nums, out int max, out int min, out int sum, out int avg, out string s)//5个out参数修饰的是多余的返回值 | |
{ | |
//out参数必须在方法内部为其赋值,否则返回去没有意义 | |
max = nums[0]; | |
min = nums[0]; | |
sum = 0; | |
for (int i = 0; i < nums.Length; i++) | |
{ | |
if (nums[i] > max) | |
{ | |
max = nums[i]; | |
} | |
if (nums[i] < min) | |
{ | |
min = nums[i]; | |
} | |
sum += nums[i]; | |
} | |
avg = sum / nums.Length; | |
//此方法void无返回值,无需写return | |
//如果方法是bool,则返回true或者false。不需要返回out中的值 | |
s = "Test_Result"; | |
} |
ref 和 out 的区别;
1、使用 ref 型参数时,传入的参数必须先被初始化。对 out 而言,传入之前不需初始化但必须在方法中对其完成初始化。
2、使用 ref 和 out 时,在方法的参数和执行方法时,都要加 Ref 或 Out 关键字(方法和参数前都要加关键字)。以满足匹配。
3、out 适合用在需要 retrun 多个返回值的地方,而 ref 则用在需要被调用的方法修改调用者的引用的时候。
如需得到一个类型或一个变量在特定平台上的准确尺寸,可以使用 sizeof 方法。表达式 sizeof(type) 产生以字节为单位存储对象或类型的存储尺寸。
方法参数传递:默认值传递
转义字符
/ : 斜杠 \ : 反斜杠
转义字符:一个” \ ” 加一个 特殊的字符,组成了一个具有特殊意义的字符
\n 表示换行 | |
\" 表示英文半角双引号 | |
\t 表示一个tab键的空格 | |
\b 表示一个退格键,放在字符串两端没有效果 | |
\r\n 表示回车并换行,写入文件进window系统时不认识 \n,只认识\r\n | |
\\ 表示一个\ |
@符号的作用:
- C# string 字符串的前面可以加 @(称作” 逐字字符串”)将转义字符(\)当作普通字符对待 ,取消 \ 的转义作用
string str = @"C:\Windows"; 等价于 string str = "C:\\Windows"; | |
//一般用于路径 | |
string path = @"E:\ljx\a-StudyC#\笔记"; |
- @ 字符串中可以任意换行,换行符及缩进空格都计算在字符串长度之内。 将字符串按照编辑的原格式输出。
System 命名空间中的 Console 类提供了一个函数 ReadLine (),用于接收来自用户的输入,并把它存储到一个变量中。
占位符
{0} 挖几个坑 就填几个坑 从 0 开始 按照填的下标数字顺序输出
运算符
逻辑与比逻辑或优先级高
一般使用逻辑与或逻辑或 ,效率更快 && ||
- 异或运算 (^) 也就是说,当且仅当只有一个操作数为 true 时,结果才为 true。
- & 运算符 (与) 两个操作数都为 true,结果才为 true
- | 运算符 (或) 只要一个操作数为 true,结果就为 true
- 左移 (<<)
将第一个操作数向左移动第二个操作数指定的位数,空出的位置补 0。
左移相当于乘。左移一位相当于乘 2; 左移两位相当于乘 4; 左移三位相当于乘 8。 - 右移 (>>)
将第一个操作数向右移动第二个操作数所指定的位数,空出的位置补 0。
右移相当于整除。右移一位相当于除以 2; 右移两位相当于除以 4; 右移三位相当于除以 8。
// ++a 先进行自增运算再赋值
int a = 5; | |
int b; | |
b = a++; //先赋值在加 | |
Console.WriteLine("a的值是:{0},b的值是:{1}",a,b); | |
Console.ReadKey(); |
?单问号,赋值 Null
? 单问号用于对 int、double、bool 等无法直接赋值为 null 的数据类型进行 null 的赋值,意思是这个数据类型是 Nullable 类型的。
int i; //默认值0 | |
int? ii; //默认值null 意思是这个数据类型是 Nullable 类型的 | |
double? num3 = new double?(); | |
double? num4 = 3.14157; | |
bool? boolval = new bool?(); |
?? 双问号判断变量为 null 返回值
?? 双问号用于判断一个变量在为 null 的时候返回一个指定的值。
Null 合并运算符用于定义可空类型和引用类型的默认值。运算符的左操作数不为 null,那么运算符将返回左操作数,否则返回右操作数。
注:Null 合并运算符左右两边操作数的类型必须相同,或者右操作数的类型可以隐式的转换为左操作数的类型(小转大),否则将编译错误。
num3 = num1 ?? 5.34; // num1不为空则返回num1,如果为空值则返回 5.34 |
?. 运算符
评估第一个操作数;如果它为空,则停止,结果为空。否则,评估第二个操作数(作为第一个操作数的成员访问)。”
checktimer?.Dispose(); //checktimer为空,则停止。如果不为空则执行Dispose()方法 |
is 和 as 区别和使用
is | 判断对象是否为某一类型。如果是则返回 true,不是则返回 false | If (Ford is Car) // 检查 Ford 是否是 Car 类的一个对象。 |
---|---|---|
as | 强制转换,即使转换失败也不会抛出异常(返回 null)。 | Object obj = new StringReader(“Hello”); StringReader r = obj as StringReader; |
条件语句
if
if-else 特点:先判断,再执行,至少都要执行一次代码
注意:else 永远跟离他最近的那个 if 配对
switch 语句
switch(变量){ | |
case 条件1: | |
break; | |
case 条件2: | |
break; | |
default: //都不满足的时候,默认是什么 | |
break; | |
} | |
//switch变形 | |
switch(变量){ | |
case 条件1: //当条件一和条件二输出内容一致时 | |
case 条件2: | |
Console.WriteLine(“两个条件为或,满足一个即可输出”); | |
} |
switch 语句,后面跟跳转语句 break 或者 continue 、go to xxx。最后面 switch 都不满足 default break 。
循环
Do While 循环
- 语法:执行过程:程序首先会执行 do 中的循环体,执行完成后,去判断 do-while 循环的循环条件,
如果成立,则继续执行 do 中的循环体,如果不成立,则跳出 do-while 循环。
do | |
{ | |
循环体; | |
}while(循环条件); |
- 特点:先循环,再判断,最少执行一遍循环体。
for 循环
下面来看看它的执行过程: | |
1. 求解表达式1。 | |
2. 求解表达式2。若其值为真,则执行 for 语句中指定的内嵌语句,然后执行第3步;若表达式2值为假,则结束循环,转到第5步。 | |
3. 求解表达式3。 | |
4. 转回上面第2步继续执行。 | |
5. 循环结束,执行 for 语句下面的语句。 | |
using System; | |
namespace zhishu; | |
class mathZS | |
{ | |
public static void Main(string[] args) | |
{ | |
int i, j; | |
//Boolean sign = true; 不可以放在循环外面 | |
int count = 0; | |
for (i = 2; i < 100; i++) | |
{ | |
Boolean sign = true; | |
for (j = 2; j < i; j++) { | |
if (i % j == 0) { | |
sign = false; | |
break; | |
} | |
} | |
if (sign) { | |
count++; | |
Console.WriteLine("1-100的质数为:{0}",i); | |
} | |
} | |
Console.WriteLine("质数有:{0}位", count); | |
} | |
} |
foreach 语句
自定义类支持 foreach
要使自定义类支持 foreach
语句,需要实现 IEnumerable<T>
接口,并且在类中定义一个返回 IEnumerator<T>
的 GetEnumerator
方法。
这个接口要求实现两个方法:
GetEnumerator()
返回一个强类型的枚举器,支持泛型集合的迭代。
IEnumerator<T>
是一个接口,定义了在泛型集合上进行迭代所需的方法和属性:bool MoveNext()
:移动到集合中的下一个元素。如果成功移动到下一个元素,则返回true
;如果已经到达集合的末尾,则返回false
。T Current
:获取集合中位于当前枚举器位置的元素。void Reset()
:将枚举器设置到它的初始位置,即集合中的第一个元素之前。通常情况下,这个方法不常使用。
- 显式实现的
IEnumerable.GetEnumerator()
返回一个非泛型的枚举器,支持非泛型集合的迭代。
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
public class MyCollection<T> : IEnumerable<T> | |
{ | |
private List<T> items = new List<T>(); | |
public void Add(T item) | |
{ | |
items.Add(item); | |
} | |
//1、泛型版本的GetEnumerator()方法,返回强类型 | |
public IEnumerator<T> GetEnumerator() | |
{ | |
return items.GetEnumerator(); | |
} | |
//2、是IEnumerable接口的显示实现,是非泛型版本 | |
//显式实现的 IEnumerable.GetEnumerator() 方法调用了泛型的 GetEnumerator() 方法 | |
//这样做的目的是支持两种接口的迭代器,而不需要重复代码: | |
IEnumerator IEnumerable.GetEnumerator() | |
{ | |
return GetEnumerator(); | |
} | |
} | |
class Program | |
{ | |
static void Main() | |
{ | |
MyCollection<int> myCollection = new MyCollection<int>(); | |
myCollection.Add(1); | |
myCollection.Add(2); | |
myCollection.Add(3); | |
foreach (int item in myCollection) | |
{ | |
Console.WriteLine(item); | |
} | |
} | |
} |
int[] fibarray = new int[] { 0, 1, 1, 2, 3, 5, 8, 13 }; | |
foreach (int element in fibarray) {方法体;} //element 元素 |
do…while 循环与 while 循环类似,但是 do…while 循环会确保至少执行一次循环。
注意:与 for 循环和 while 循环不同,do while 循环需要以分号;结尾。
C# 中的 continue 语句有点像 break 语句。但它不是强迫终止,continue 会跳过当前循环中的代码,强迫开始下一次循环。
goto 语句用法
使用 goto 语句来跳转程序,必须先在想要跳转的位置定义好一个标签(Labels),标签名称的定义和变量名类似,然后使用 goto 标签名;即可使程序跳转到指定位置执行。
提示:goto 语句并不限于在循环中使用,其它的情况也可以使用。但是,goto 语句不能从循环外跳转到循环语句中,而且不能跳出类的范围。
/* goto Label; | |
语句块 1; | |
Label: | |
语句块 2; */ | |
login: | |
Console.Writeline("登录啦"); | |
goto login; | |
Console.WriteLine("登录成功"); |
封装
封装 被定义为” 把一个或多个项目封闭在一个物理的或者逻辑的包中”。在面向对象程序设计方法论中,封装是为了防止对实现细节的访问。
抽象和封装是面向对象程序设计的相关特性。抽象允许相关信息可视化,封装则使开发者实现所需级别的抽象。
一个 访问修饰符 定义了一个类成员的范围和可见性。C# 支持的访问修饰符如下所示:
- public:所有对象都可以访问;
- private:对象本身在对象内部可以访问;
- protected:只有该类对象及其子类对象可以访问
- internal:同一个程序集的对象可以访问;(只能在同一个命名空间中可以调用)
- protected internal:访问限于当前程序集或派生自包含类的类型。 允许在本类,派生类或者包含该类的程序集中访问。这也被用于实现继承。
声明命名空间、类,前面不加限制访问修饰符时,默认访问权限为 internal—— 访问仅限于当前程序集。
声明类成员 (包括字段 (变量)、属性和方法) 默认为 private)以及结构类型,前面不加限制访问修饰符时,默认访问权限为 private—— 访问仅限于当前类。如果没有指定访问修饰符,则使用类成员的默认访问修饰符,即为 private。
基本集合和数据结构
数组
数组是一个存储相同类型元素的固定大小的顺序集合。
数组是一个引用类型,所以您需要使用 new 关键字来创建数组的实例。
必须在声明的时候指定容量。过长导致浪费,过短无法使用。数组是一段连续的索引空间,因此查询数据很快。
您创建一个数组时(必须指定数组大小,因为数组是固定的),C# 编译器会根据数组类型隐式初始化每个数组元素为一个默认值。例如,int 数组的所有元素都会被初始化为 0。
//初始化数组并且设定数组大小且赋值 | |
double[] arr1 = new double[4]{96.5, 98.0, 99.5, 90.0}; |
概念 | 描述 |
---|---|
多维数组 | C# 支持多维数组。多维数组最简单的形式是二维数组。 |
交错数组 | C# 支持交错数组,即数组的数组。 |
传递数组给函数 | 您可以通过指定不带索引的数组名称来给函数传递一个指向数组的指针。 |
参数数组 | 这通常用于传递未知数量的参数给函数。 |
Array 类 | 在 System 命名空间中定义,是所有数组的基类,并提供了各种用于数组的属性和方法。 |
Array 类
Array 类是 C# 中所有数组的基类,它是在 System 命名空间中定义。
- 下表列出了 Array 类中属性和常用方法
ARRAY 类最常用的属性 | |
---|---|
IsFixedSize | 获取一个值,该值指示数组是否带有固定大小。 |
IsReadOnly | 获取一个值,该值指示数组是否只读。 |
Length | 获取一个 32 位整数,该值表示所有维度的数组中的元素总数。 |
LongLength | 获取一个 64 位整数,该值表示所有维度的数组中的元素总数。 |
Rank | 获取数组的秩(维度)。 |
序号 | 方法 & 描述 |
---|---|
1 | Clear 根据元素的类型,设置数组中某个范围的元素为零、为 false 或者为 null。 |
2 | Copy(Array, Array, Int32) 从数组的第一个元素开始复制某个范围的元素到另一个数组的第一个元素位置。长度由一个 32 位整数指定。 |
3 | CopyTo(Array, Int32) 从当前的一维数组中复制所有的元素到一个指定的一维数组的指定索引位置。索引由一个 32 位整数指定。 |
4 | GetLength 获取一个 32 位整数,该值表示指定维度的数组中的元素总数。 |
5 | GetLongLength 获取一个 64 位整数,该值表示指定维度的数组中的元素总数。 |
6 | GetLowerBound 获取数组中指定维度的下界。 |
7 | GetType 获取当前实例的类型。从对象(Object)继承。 |
8 | GetUpperBound 获取数组中指定维度的上界。 |
9 | GetValue(Int32) 获取一维数组中指定位置的值。索引由一个 32 位整数指定。 |
10 | IndexOf(Array, Object) 搜索指定的对象,返回整个一维数组中第一次出现的索引。 |
11 | Reverse(Array) 逆转整个一维数组中元素的顺序。 |
12 | SetValue(Object, Int32) 给一维数组中指定位置的元素设置值。索引由一个 32 位整数指定。 |
13 | Sort(Array) 使用数组的每个元素的 IComparable 实现来排序整个一维数组中的元素。 |
14 | ToString 返回一个表示当前对象的字符串。从对象(Object)继承。 |
交错数组:在声明交错数组的时候,只需要指定第一维的长度 int[ ] [ ] arr = new int [ 5 ] [ ];
- 交错数组是一个数组的数组,每一行都是一个独立的数组。
- 每一行的数组长度可以不同,因此每一行的数组都是独立分配内存的。
- 在内存中,各行的数组不一定是连续的,每一行的数组都有自己的内存地址。
参数数组:在使用数组作为形参时,C# 提供了 params 关键字,使调用数组为形参的方法时,既可以传递数组实参,也可以传递一组数组元素。params 的使用格式为: public int AddElements (params int[] arr)
params
关键字在 C# 中用于指定一个方法参数可以接受可变数量的参数。它允许你传递任意数量的参数(包括零个)给该方法,这些参数在方法内部会被当作数组来处理。
ArrayList
ArrayList 的出现解决了部分数组出现的问题,在声明的时候不必指定容量,添加了 Add 方法。方便插入数据
但是此时出现了新的问题,不安全类型以及装箱,拆箱导致的性能浪费。
- 使用 ArrayList 可以插入字符串已经数字 ,比如 Add (“abc”); Add (123); 这在 ArrayList 是允许的,因为他会将插入的数据转换为 object 类型 来处理,那在使用的时候,很可能会报类型不匹配的错误,这就是类型不安全
- 在使用的时候,会发生装箱和拆箱的操作,很影响性能损耗
List
- 允许存储重复的元素。
- 元素按照插入顺序存储,可以通过索引访问元素。
- 插入和删除元素的性能较好,因为它使用数组来存储元素,但查找元素的性能较差。
Set
1、 HashSet
- 保证集合中的元素是唯一的,不允许重复。
- 不保证元素的顺序,元素存储的顺序与插入顺序不一定相同。
- 查找元素的性能较好,因为它使用哈希表来存储元素,可以通过哈希值快速定位元素。
2、SortedSet
- 保证集合中的元素是唯一的,不允许重复。
- 元素按照升序存储,集合中的元素总是保持有序状态,不支持通过索引访问元素。
- 查找、插入和删除元素的性能较好,因为它使用红黑树来存储元素,这保证了元素的有序性,并且这些操作的时间复杂度为 O (log n)。
Dictionary – 字典
字符串
字符串拼接
1. 使用字符串插值 – $”…” 的方式 (推荐使用)
适合大多数情况,尤其是当你需要插入多个变量或表达式时,代码更加简洁和易读。
string value = "wordpad.exe"; | |
Console.WriteLine($"For key = \"rtf\", value = {value}"); |
2. 使用 String.Format 方法
适合需要复杂格式化的场景,尤其是格式字符串和变量位置较多时。
string value = "wordpad.exe"; | |
Console.WriteLine("For key = \"rtf\",value = {0}",value); |
3. 使用 + 拼接
string value = "wordpad.exe"; | |
Console.WriteLine("For key = \"rtf\",value = " + value); |
- 性能问题:每次使用 + 操作符拼接字符串都会创建一个新的字符串对象,导致不必要的内存开销,特别是在循环中大量使用时。
4. 性能优化
在大量拼接情况下,使用 StringBuilder 是最佳选择。
StringBuilder 是可变的,因此在进行多次修改时,不会创建新的对象,而是在原有对象上进行修改,这样可以避免频繁的内存分配和拷贝,提高了性能。
因此,在需要进行频繁字符串拼接的场景下,特别是在循环中,推荐使用 StringBuilder 来提高性能。
示例代码:
static void Main() | |
{ | |
StringBuilder sb = new StringBuilder(); | |
// 在 StringBuilder 中添加文本 | |
sb.Append("Hello, "); | |
sb.Append("world!"); | |
// 可以直接通过索引器访问和修改字符串中的字符 | |
sb[7] = 'W'; | |
// 在指定位置插入文本 | |
sb.Insert(6, "beautiful "); | |
// 删除指定位置开始的一定长度的字符 | |
sb.Remove(12, 1); | |
// 清空 StringBuilder | |
// sb.Clear(); | |
// 获取 StringBuilder 中的字符串 | |
string result = sb.ToString(); | |
Console.WriteLine(result); // 输出:Hello, beautiful World! | |
} |
StringBuilder 的常用方法如下:
- Append(string value):将指定字符串追加到 StringBuilder 的末尾。
- Insert(int index, string value):在指定位置插入指定字符串。
- Remove(int startIndex, int length):从指定位置开始删除指定长度的字符。
- Clear():清空 StringBuilder 中的内容。
- ToString():将 StringBuilder 对象转换为字符串。
trim 的用法
属性 | 描述 |
---|---|
Trim(Char[]) | 从当前字符串删除数组中指定的一组字符的所有前导匹配项和尾随匹配项。 |
Trim(char) | 从当前字符串删除字符的所有前导实例和尾随实例。 |
Trim() | 从当前字符串删除所有前导空白字符和尾随空白字符。 |
TrimEnd(Char[]) | 删除字符串尾部的空白字符 |
TrimStart(Char[]) | 删除字符串首部的空白字符 |
格式化字符串
- 转化成时间
//用于转化值的格式化方法 | |
DateTime waiting = new DateTime(2012, 10, 10, 17, 58, 1); | |
string chat = String.Format("Message sent at {0:t} on {0:D}", | |
waiting); | |
//结果 Message: Message sent at 17:58 on 2012年10月10日 |
- 数字
//保留小数点后两位 | |
ToString("0.00") | |
//保留小数点后四位 | |
ToString("f4"); | |
//将数字以大写的十六进制形式输出,至少保证两个字符。如果数字很大,可能会有两个字符以上输出 | |
ToString("X2"); |
String 类的方法
方法 | 描述 |
---|---|
Clone() | 返回对此 String 实例的引用 |
Compare(String, String) | 比较两个指定的 String 对象,并返回一个指示二者在排序顺序中的相对位置的整数。该方法区分大小写。 |
CompareOrdinal(String, String) | 通过比较每个字符串中的字符,来比较两个字符串是否相等 |
CompareTo(String) | 将一个字符串与另一个字符串进行比较 |
Concat(String, String) | 连接两个指定的字符串 |
Contains(String) | 判断一个字符串中是否包含零一个字符串 |
Copy(String) | 将字符串的值复制一份,并赋值给另一个字符串 |
CopyTo(Int32, Char[], Int32, Int32) | 从字符串中复制指定数量的字符到一个字符数组中 |
EndsWith(String) | 用来判断字符串是否以指定的字符串结尾 |
Equals(String, String) | 判断两个字符串是否相等 |
Format(String, Object) | 将字符串格式化为指定的字符串表示形式 |
GetEnumerator() | 返回一个可以循环访问此字符串中的每个字符的对象 |
GetHashCode() | 返回该字符串的哈希代码 |
GetType() | 获取当前实例的类型 |
GetTypeCode() | 返回字符串的类型代码 |
IndexOf(String) | 返回字符在字符串中的首次出现的索引位置,索引从零开始 |
Insert(Int32, String) | 在字符串的指定位置插入另一个字符串,并返回新形成的字符串 |
Intern(String) | 返回指定字符串的内存地址 |
IsInterned(String) | 返回指定字符串的内存地址 |
IsNormalized() | 判断此字符串是否符合 Unicode 标准 |
IsNullOrEmpty(String) | 判断指定的字符串是否为空(null)或空字符串(””) |
IsNullOrWhiteSpace(String) | 判断指定的字符串是否为 null、空或仅由空白字符组成 |
Join(String, String[]) | 串联字符串数组中的所有元素,并将每个元素使用指定的分隔符分隔开 |
LastIndexOf(Char) | 获取某个字符在字符串中最后一次出现的位置 |
LastIndexOfAny(Char[]) | 获取一个或多个字符在字符串中最后一次出现的位置 |
Normalize() | 返回一个新字符串,新字符串与原字符串的值相等,但其二进制表示形式符合 Unicode 标准 |
PadLeft(Int32) | 返回一个指定长度的新字符串,新字符串通过在原字符串左侧填充空格来达到指定的长度,从而实现右对齐 |
PadRight(Int32) | 返回一个指定长度的新字符串,新字符串通过在原字符串右侧填充空格来达到指定的长度,从而实现左对齐 |
Remove(Int32) | 返回一个指定长度的新字符串,将字符串中超出长度以外的部分全部删除 |
Replace(String, String) | 使用指定字符替换字符串中的某个字符,并返回新形成的字符串 |
Split(Char[]) | 按照某个分隔符将一个字符串拆分成一个字符串数组 |
StartsWith(String) | 判断字符串是否使用指定的字符串开头 |
Substring(Int32) | 从指定的位置截取字符串 |
ToCharArray() | 将字符串中的字符复制到 Unicode 字符数组 |
ToLower() | 将字符串中的字母转换为小写的形式 |
ToLowerInvariant() | 使用固定区域性的大小写规则将字符串转换为小写的形式 |
ToString() | 将其它数据类型转换为字符串类型 |
ToUpper() | 将字符串中的字母转换为大写形式 |
PadLeft 用法
1).PadLeft (Int32) 左边补齐
返回一个新字符串,该字符串通过在此实例中的字符左侧填充空格来达到指定的总长度,从而实现右对齐。
2).PadLeft(Int32, Char)
返回一个新字符串,该字符串通过在此实例中的字符左侧填充指定的 Unicode 字符来达到指定的总长度,从而使这些字符右对齐。(实例:string num=12 num.PadLeft (4, ‘0’); // 结果为为‘0012’ // 看字符串长度是否满足 4 位,不满足则在字符串左边以”0″补足)
PadLeft (int totalWidth, char paddingChar) // 在字符串左边用 paddingChar 补足 totalWidth 长度
PadRight (int totalWidth, char paddingChar) // 在字符串右边用 paddingChar 补足 totalWidth 长度
Substring 用法
string path = "http:www.baidu.com"; | |
Console.WriteLine(path.Substring( 3 )); //截取掉 htt | |
//输出为 p:www.baidu.com |
结构
结构体是值类型数据结构。它使得一个单一变量可以存储各种数据类型的相关数据。struct 关键字用于创建结构体。
结构的特点:
- 结构可带有方法、字段、索引、属性、运算符方法和事件。
- 结构可定义构造函数,但不能定义析构函数。但是,您不能为结构定义无参构造函数。无参构造函数 (默认) 是自动定义的,且不能被改变。
- 与类不同,结构不能继承其他的结构或类。
- 结构不能作为其他结构或类的基础结构。
- 结构可实现一个或多个接口。
- 结构成员不能指定为 abstract、virtual 或 protected。
- 当您使用 New 操作符创建一个结构对象时,会调用适当的构造函数来创建结构。与类不同,结构可以不使用 New 操作符即可被实例化。(不能在声明成员属性时对它们进行初始化,静态属性和常量除外;)
- 如果不使用 New 操作符,只有在所有的字段都被初始化之后,字段才被赋值,对象才被使用。
类 vs 结构
类和结构有以下几个基本的不同点:
- 类是引用类型,结构是值类型。
- 结构不支持继承。
- 结构不能声明默认的构造函数。
析构函数
C# 中的析构函数具有以下特点:
- 析构函数只能在类中定义,不能用于结构体;
- 一个类中只能定义一个析构函数;
- 析构函数不能继承或重载;
- 析构函数没有返回值;
- 析构函数是自动调用的,不能手动调用;
- 析构函数不能使用访问权限修饰符修饰,也不能包含参数。
类的 析构函数 是类的一个特殊的成员函数,当类的对象超出范围时执行。
析构函数的名称是在类的名称前加上一个波浪形(~)作为前缀,它不返回值,也不带任何参数。
析构函数用于在结束程序(比如关闭文件、释放内存等)之前释放资源。析构函数不能继承或重载。
~Line() //析构函数 | |
{ | |
Console.WriteLine("对象已删除"); | |
} |
构造函数
静态构造函数(可以实现单例模式)
静态构造函数具有以下特性:
- 静态构造函数不使用访问权限修饰符修饰或不具有参数;
- 类或结构体中只能具有一个静态构造函数;
- 静态构造函数不能继承或重载;
- 静态构造函数不能直接调用,仅可以由公共语言运行时 (CLR) 调用;(且只会调用一次)
- 用户无法控制程序中静态构造函数的执行时间;
- 在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数以初始化类;
- 静态构造函数会在实例构造函数之前运行。
类
new 关键字在这里的作用主要是在程序运行时为类的实例分配内存。
Student Object = new Student(); |
类的默认访问标识符是 internal,成员的默认访问标识符是 private。
static
我们可以使用 static 关键字把类成员定义为静态的。当我们声明一个类成员为静态时,意味着无论有多少个类的对象被创建,只会有一个该静态成员的副本。
关键字 static 意味着类中只有一个该成员的实例。静态变量用于定义常量,因为它们的值可以通过直接调用类而不需要创建类的实例来获取。静态变量可在成员函数或类的定义外部进行初始化。你也可以在类的定义内部初始化静态变量。
静态函数只能访问静态属性。
枚举
//不能定义在方法中 | |
enum enum_name{ | |
enumeration list; //多个值以逗号隔开 | |
} | |
//默认情况下,枚举类型中的每个成员都为 int 类型,它们的值从零开始,并按定义顺序依次递增。 | |
foreach(Season i in Enum.GetValues(typeof(Season))){ //GetValues | |
Console.WriteLine("{0} = {1}", i, (int)i); | |
} | |
foreach(String s in Enum.GetNames(typeof(Season))){ //GetNames | |
Console.WriteLine(s); | |
} |
this 关键字
1) 使用 this 表示当前类的对象
2) 使用 this 关键字串联构造函数
public Test() | |
{ | |
Console.WriteLine("无参构造函数"); | |
} | |
// 这里的 this()代表无参构造函数 Test() | |
// 先执行 Test(),后执行 Test(string text) | |
public Test(string text) : this() | |
{ | |
Console.WriteLine(text); | |
Console.WriteLine("实例构造函数"); | |
} | |
//输出 无参构造函数 | |
// text | |
// 实例构造函数 |
3) 使用 this 关键字作为类的索引器
4) 使用 this 关键字作为原始类型的扩展方法
继承
一个类可以派生自多个类或接口,这意味着它可以从多个基类或接口继承数据和函数。
C# 中创建派生类的语法如下:
class <派生类> : <基类> | |
{ | |
... | |
} |
C# 不支持多重继承。但是,您可以使用接口来实现多重继承。
接口
接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。
接口使用 interface 关键字声明,它与类的声明类似。接口声明默认是 public 的。添加修饰符会报错
通常接口命令以 I 字母开头 (不是必须的)。但希望你使用这个默认的写法
接口继承:一个接口可以继承另外一个接口
partial
Partial 关键词定义的类可以在多个地方被定义,最后编译的时候会被当作一个类来处理。
多态
多态是同一个行为具有多个不同表现形式或形态的能力。
多态性意味着有多重形式。在面向对象编程范式中,多态性往往表现为” 一个接口,多个功能”。
静态多态性 1. 函数重载 2. 运算符重载
在编译时,函数和对象的连接机制被称为早期绑定,也被称为静态绑定
1. 函数重载函数的定义必须彼此不同,可以是参数列表中的参数类型不同,也可以是参数个数不同。不能重载只有返回类型不同的函数声明。
2. 运算符重载: 通过关键字 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; | |
} |
注意:比较运算符必须成对重载,也就是说,如果重载一对运算符中的任意一个,则另一个运算符也必须重载。比如 ==
和 !=
运算符、<
和 >
运算符、<=
和 >=
运算符。
动态多态性 抽象类
C# 允许您使用关键字 abstract 创建抽象类,用于提供接口的部分类的实现。当一个派生类继承自该抽象类时,实现即完成。抽象类包含抽象方法,抽象方法可被派生类实现。
下面是有关抽象类的一些规则:
- 您不能创建一个抽象类的实例。
- 您不能在一个抽象类外部声明一个抽象方法。
- 通过在类定义前面放置关键字 sealed,可以将类声明为密封类。当一个类被声明为 sealed 时,它不能被继承。抽象类不能被声明为 sealed。
当有一个定义在类中的函数需要在继承类中实现时,可以使用虚方法。虚方法是使用关键字 virtual 声明的。虚方法可以在不同的继承类中有不同的实现 (重写虚方法)。对虚方法的调用是在运行时发生的。动态多态性是通过 抽象类 和 虚方法 实现的。
重写
- 重写父类的方法要用到 override 关键字 (具有 override 关键字修饰的方法是对父类中同名方法的新实现)
- 给父类中要被重写的方法添加 virtual (虚拟) 关键字表示可以在子类中重写它的实现。C# 中的方法默认并不是 virtual (虚拟) 类型的,因此要添加 virtual (虚拟) 关键字才能够被重写,abstract (抽象) 也可以.
- virtual (虚拟) 关键字用于将方法定义为支持多态,有 virtual (虚拟) 关键字修饰的方法称为 “虚拟方法”
重写时父类中的语法:
[访问修饰符] virtual [返回类型] 方法名(参数列表) | |
{ | |
//虚拟方法的实现,该方法可以被子类重写 | |
} |
override(重写)与 overload(重载)的区别?
定义上的区别:
- 重载是指不同的函数使用相同的函数名,但是函数的参数个数或类型以及顺序不同。调用的时候根据函数的参数来区别不同的函数。
- 重写(也叫覆盖)是指在子类中重新对父类中的虚拟函数或者抽象函数重新实现。即函数名和参数都一样,只是函数的实现体不一样。
命名空间
为了调用支持命名空间版本的函数或变量,会把命名空间的名称置于前面,如下所示:
namespace_name.item_name; |
命名空间可以嵌套使用,也就是说我们可以在一个命名空间中再定义一个或几个命名空间
using
using 关键字表明程序使用的是给定命名空间中的名称。
可以使用 using 命名空间指令,这样在使用的时候就不用在前面加上命名空间名称。该指令告诉编译器随后的代码使用了指定命名空间中的名称。
内嵌命名空间:命名空间可以被嵌套,即您可以在一个命名空间内定义另一个命名空间
using 语句用法:
在这个代码段中使用了 using 语句,它的作用是当使用 SqlConnection 类的实例时无论什么原因,离开这个代码段就自动调用 SqlConnection 类的 Dispose。使用 try……catch 也能达到同样的目的,但是 using 更为方便一点。也可以这样理解:using(SqlConnection con = new SqlConnection (strCon))这句代码的意思是控制对于数据库的关闭和释放,如果是其他的内容也是同理。
在本代码中,如果 using 下的代码段产生错误,直接结束整段 using 下的代码,并且关闭数据库并释放资源。类似于 try……catch,但是更为高级。因为在 try……catch 中如果产生某句代码产生异常,会在该句产生一个断点,中断于此并抛出异常。而且如果要释放资源,需要在 catch 中声明和定义。但是 using 会自动的释放掉这些占用的资源。
预处理器指令
预处理器指令 | 描述 |
---|---|
#define | 用于定义一系列字符,可以将这些字符称为符号 |
#undef | 用于取消一个已定义符号 |
#if | 用于测试符号是否为真 |
#else | 用于创建复合条件指令,与 #if 一起使用 |
#elif | 用于创建复合条件指令 |
#endif | 指定一个条件指令的结束 |
#line | 用于修改编译器的行数以及(可选地)输出错误和警告的文件名 |
#error | 用于在代码的指定位置生成一个错误 |
#warning | 用于在代码的指定位置生成一级警告 |
#region | 用于在使用 Visual Studio Code Editor 的大纲特性时,指定一个可展开或折叠的代码块 |
#endregion | 用于标识 #region 块的结束 |
可以使用 #if 来创建条件指令,条件指令可以用于测试一个或多个符号的值是否为 true 。如果符号的值为 true,那么编译器将评估 #if 指令和下一个指令之间的所有代码。
#define PI //用来定义一个字符 | |
#if (PI) | |
// 要执行的代码 | |
#elif (PI) | |
// 要执行的代码 | |
#else | |
// 要执行的代码 | |
#endif //以 #if 指令开头的条件指令必须以 #endif 指令显式结束。 |
异常
C# 异常是使用类来表示的。C# 中的异常类主要是直接或间接地派生于 System.Exception 类。System.ApplicationException 和 System.SystemException 类是派生于 System.Exception 类的异常类。
System.SystemException 类是所有预定义的系统异常的基类。
自行定义异常类,自定义的异常类都应继承 System.ApplicationException 类。
异常是在程序运行出错时引发的,例如以一个数字除以零,所有异常都派生自 System.Exception 类。异常处理则是处理运行时错误的过程,使用异常处理可以使程序在发生错误时保持正常运行。
额
IO 文件基本操作
读写五步操作
//1.创建文件流 | |
FileStream fs(<object_name>) = new FileStream(<file_name>, <FileMode Enumerator>, <FileAccess Enumerator>, <FileShare Enumerator>); | |
//2.创建写入流 | |
StreamWriter sw = new StreamWriter(fs); | |
//3.以流的方式写入文件 | |
sw.Write();//一次性全部写入 sw.WriteLine() 写入数据并追加一个换行符 | |
//4.关闭写入流 | |
sw.Close(); | |
//5.关闭文件流 | |
fs.Close(); | |
//读取方法有很多种 Read返回索引 ReadLine 读取一行 ReadToEnd 读取到结尾 | |
sr.ReadToEnd(); |
文件的删除、复制、移动
// 在项目升级的时候会使用到,有新文件就将旧的文件删除。优化:将旧的保存,以防万一
//复制文件(全路径包含拓展名) | |
//ps:目标路径必须创建好目录,这个方法只是创建文件而已.如果目标文件已存在也会报错 | |
File.Copy(txt_sourceFilePath.Text.ToString().Trim(),txt_destinationFilePath.Text.ToString().Trim()); | |
File.Delete(txt_sourceFilePath.Text.ToString().Trim()); |
目录操作
显示指定目录下的文件
显示指定目录下的子目录 // 如果看到最后的目录,可以使用递归
删除目录
//Directory 从相对路径 debug 目录下找
string[] files = Directory.GetFiles("123");
// 从绝对路径查找
FileInfo[] files2 = new DirectoryInfo("D:\\123").GetFiles();
// 查找子目录,如果想找到想要的目录可以使用递归算法
string[] directory = Directory.GetDirectories("D:\\");
// 要求目录必须为空
Directory.Delete(目录路径);
// 可以强制删除非空目录 DirectoryInfo 还有其他方法,可以翻阅文档查看
DirectoryInfo dr = new DirectoryInfo(目录路径);
dr.Delete(true);
对象的保存
将界面的数据保存为对象存储在文件中,需要的时候从文件读取
缺点:
- 信息透明,都可以查看,安全性差。
- 修改属性的时候需要经常修改对象
序列化对象
先将对象标识为可序列化
类上面+ [Serializable]
//实例化文件流 | |
FileStream fs = new FileStream("obj.stu",FileMode.Open); | |
//二进制格式化器 | |
BinarryFormatter formatter = new BinarryFormatter(); | |
//序列化对象 | |
formatter.Serialize(fs,对象); | |
//关闭 | |
fs.Close(); | |
//反序列化对象 返回对象是一个Object 强制转换为对象 | |
People people = (People)formatter.Deserialize(fs); |
FileStream 类
FileStream 类在 System.IO 命名空间下,使用它可以读取、写入和关闭文件。创建 FileStream 类对象的语法格式
FileStream <object_name> = new FileStream(<file_name>, <FileMode Enumerator>, <FileAccess Enumerator>, <FileShare Enumerator>); |
参数说明如下:
- object_name:创建的对象名称;
- file_name:文件的路径(包含文件名在内);
- FileMode:枚举类型,用来设定文件的打开方式,可选值如下:
- Append:打开一个已有的文件,并将光标放置在文件的末尾。如果文件不存在,则创建文件;
- Create:创建一个新的文件,如果文件已存在,则将旧文件删除,然后创建新文件;
- CreateNew:创建一个新的文件,如果文件已存在,则抛出异常;
- Open:打开一个已有的文件,如果文件不存在,则抛出异常;
- OpenOrCreate:打开一个已有的文件,如果文件不存在,则创建一个新的文件并打开;
- Truncate:打开一个已有的文件,然后将文件清空(删除原有内容),如果文件不存在,则抛出异常。
- FileAccess:枚举类型,用来设置文件的存取,可选值有 Read、ReadWrite 和 Write;
- FileShare:枚举类型,用来设置文件的权限,可选值如下:
- Inheritable:允许子进程继承文件句柄,Win32 不直接支持此功能;
- None:在文件关闭前拒绝共享当前文件,打开该文件的任何请求(由此进程或另一进程发出的请求)都将失败;
- Read:允许随后打开文件读取,如果未指定此标志,则文件关闭前,任何打开该文件以进行读取的请求都将失败,需要注意的是,即使指定了此标志,仍需要附加权限才能够访问该文件;
- ReadWrite:允许随后打开文件读取或写入,如果未指定此标志,则文件关闭前,任何打开该文件以进行读取或写入的请求都将失败,需要注意的是,即使指定了此标志,仍需要附加权限才能够访问该文件;
- Write:允许随后打开文件写入,如果未指定此标志,则文件关闭前,任何打开该文件以进行写入的请求都将失败,需要注意的是,即使指定了此标志,仍可能需要附加权限才能够访问该文件;
- Delete:允许随后删除文件。
StreamReader 类方法
- string ReadLine(),方法,读取数据直到遇到换行符 (Unix) 或回车换行符 (Windows);
- string ReadToEnd(),方法,读取到文件尾的全部数据
XML 文件操作
- 实例化 XML 文件对象
- 加载 XML 文件到文档对象中
- 找到根节点
- 遍历跟节点
获取指定节点的值
如果节点里面有多个属性,可以获取值
假设我现在需要下面这个 xml 文件中 Xoffset 的值
<SecondOffsetData Section="上料机械臂第一层吸料位置偏移" Xoffset="0.5" Yoffset="0.7" Zoffset="0" Aoffset="0" Boffset="0" Coffset="0" /> |
使用代码:string s = node.Attributes["Xoffset"].InnerText;
属性
快捷键:
prop tab + tab
propfull 出现属性完整代码
有 get,set 方法的称作属性
public int MyProperty { get; set; } |
抽象类可拥有抽象属性,这些属性应在派生类中被实现。
public abstract string Name | |
{ | |
get; | |
set; | |
} |
Implicit 和 Explicit
Implicit
关键字用于声明隐式的用户定义类型转换运算符。如果可以确保转换过程不会造成数据丢失,则可使用该关键字在用户定义类型和其他类型之间进行隐式转换。隐式转换可以通过消除不必要的类型转换来提高源代码的可读性。 但是,因为隐式转换不需要程序员将一种类型显式强制转换为另一种类型,所以使用隐式转换时必须格外小心,以免出现意外结果。 一般情况下,隐式转换运算符应当从不引发异常并且从不丢失信息,以便可以在程序员不知晓的情况下安全使用它们。 如果转换运算符不能满足那些条件,则应将其标记为 explicit。
//没有使用Implicit | |
public class DateTimeRange | |
{ | |
public DateTime StartTime { get; set; } | |
public DateTime EndTime { get; set; } | |
public DateTimeRange(DateTime startTime, DateTime endTime) | |
{ | |
StartTime = startTime; | |
EndTime = endTime; | |
} | |
} | |
(timeRange.EndTime - timeRange.StartTime).TotalHours; //使用 获取小时 | |
//使用Implicit | |
public class DateTimeRange | |
{ | |
public DateTime StartTime { get; set; } | |
public DateTime EndTime { get; set; } | |
public DateTimeRange(DateTime startTime, DateTime endTime) | |
{ | |
StartTime = startTime; | |
EndTime = endTime; | |
} | |
//operator 后面跟需要转换的类型 | |
public static implicit operator double(DateTimeRange timeRange) | |
{ | |
return (timeRange.EndTime - timeRange.StartTime).TotalHours; | |
} | |
} | |
double hours = timeRange;//使用Implicit 隐式类型转换 | |
double hours = (double)timeRange;//使用Explicit 显示类型转换 |
Explicit
explicit 关键字用于声明必须使用强制转换来调用的用户定义的类型转换运算符
文章转载来源于C#详细学习教程
标签:入门,C#,基础,指定,类型,int,数组,使用,字符串 From: https://blog.csdn.net/gjw3037109961/article/details/140416346