一、基础
Ⅰ 关键字
1、record
record(记录),编译器会在后台创建一个类。支持类似于结构的值定义,但被实现为一个类,方便创建不可变类型,成员在初始化后不能再被改变 (C#9新增)
在运行时通过构造函数给成员赋值
2、init
init关键字,代替set(C#9新增)
特性:只能通过构造函数和对象初始化器来设置属性值
public string Name { get; init; }
若用旧的.NET框架版本使用.NET 5代码,需手动添加虚拟类,如下:
namespace System.Runtime.CompilerServices
{
internal static class IsExternalInit { }
}
3、with
with关键字(C#9新增),内部调用<>Clone浅copy方法,使用如下:
MyMath myMath2=myMath1 with { };//浅拷贝
4、base
base关键字,
作用1:子类实例化时默认调用父类的无参构造函数,base可指定调用父类对应的有参构造函数;
作用2:在子类中通过base调用父类被子类重写的虚方法;
5、params
params,修饰方法参数,
1、被修饰的参数必须为一维数组
2、被修饰参数为最后参数,后面不允许有其它参数
6、ref、out
1、被ref或out修饰的参数通过引用方式传递;
2、传参时也必须带关键字ref(out);
3、ref修饰的参数传参时必须提前定义并初始化,out可在传参时定义;
4、out修饰的参数必须在控制离开方法之前对该参数赋值;
7、sealed
sealed,有封装的意思,一般用于修饰类或方法
1、修饰类时,该类不可被继承;
2、修饰方法时,一般用于virtual与抽象方法的继承类,sealed一般与override同时存在,被修饰的已重写的方法不可被其派生类重写。
8、lock
lock, 确保代码正常执行,不会被其他线程中断;将代码定义为互斥段,同一时刻只能由一个线程执行,其他线程必须等待(解决多线程同时抢占同一资源产生的冲突问题)
示例如下:
_root:指需要跟踪的对象(通常实例化一个object作为跟踪对象)
{ _list.Clear(); }:指定义为互斥段的代码块;
lock(_root)
{
_list.Clear();
}
9、readonly
readonly,用于修饰字段为只读字段 :
1、在运行时可通过构造函数赋值;
Ⅱ 特性
特性,指C#中对类及程序集成员的进一步描述:
- [Key],指示该属性为主键(仅对属性、字段、索引器有效);
- [StringLength(maximumLength:50,MinimumLength =2)],限制字符串长度(仅对属性、字段、参数、索引器有效);
- [EmailAddress],识别邮箱格式(仅对属性、字段、参数、索引器有效);
- [Required],不可为空(仅对属性、字段、参数、索引器有效);
- [Display(Name="电话号码")],显示别名;
- [CallerMemberName],获取调用该方法的成员名(仅对参数有效);
- [CallerFilePath],获取调用该方法的文件路径(仅对参数有效);
- [CallerLineNumber],获取调用该方法的行数(仅对参数有效);
- [Compare(“Password”, ErrorMessage = “”)],比较两个属性的值是否相同;
- [DataType(DataType.Password)],标记某属性为密码类型;
三、扩展知识
1、Predicate
拥有一个或多个泛型参数并返回一个 bool 值,常用于对 collection 进行一组条件检索,类似于Func。
举例:Predicate pre=m=>m.Id==2;
2、设置C#语言版本
工程文件 x.csproj中修改
PropertyGroup节点内添加子节点:
<LangVersion>latest</LangVersion>
3、ListCollectionView过滤集合
使用ListCollectionView类构造函数注入列表
通过该类的 Filter属性过滤集合
List<Animal> animals = new List<Animal>() { new Animal(1,"ani1"),new Animal(2,"动物2") };
List<Bear> bears = new List<Bear>();
var tmp = animals.Adapt<List<Bear>>();
tmp.ForEach(m => m.Description = "Animal adapt bear...");
ListCollectionView view=new ListCollectionView(tmp);
view.Filter = i => ((Bear)i).ID == 2;
foreach (var animal in view)
MessageBox.Show(((Bear)animal).Name);
4、Adapt适配器
安装NutGet包:Mapster
可理解成转换器,适配器适配的是不同类间相同的名称,不论字段或属性(必须为值类型或字符串类型),只要名字相同,都适配给目的对象;
注意:即使名称相同,属性或字段也不能适配成方法
Animal animal = new Animal(18);
Bear bear = animal.Adapt<Bear>();
Console.WriteLine(bear.Age.ToString());
Console.WriteLine(bear.Description.ToString());
Console.WriteLine("************************");
Bear bear1=new Bear();
Console.WriteLine(bear1.Age.ToString());
Console.WriteLine(bear1.Description.ToString());
Console.WriteLine("*************************");
Banana banana = animal.Adapt(new Banana());
Console.WriteLine(banana.Description);
5、值类型与引用类型
值类型:变量直接保存其数据,作为类的字段(成员变量)时,跟随其所属的实例存储,也就是存储在堆中;作为方法中的局部变量时,存储在栈上;
引用类型:变量保存其数据的引用(地址)分配在栈中,具体数据(实例)部署在托管堆中;
值类型:结构体(数值类型,bool型,用户定义的结构体),枚举,可空类型
引用类型:数组,用户定义的类、接口、委托,object,字符串
引用类型string:
string a = "A";
string b = a;
Console.WriteLine($"a:{a}\tb:{b}");
a= "B";
Console.WriteLine($"a:{a}\tb:{b}");
string为引用类型,上面示例看出string像值类型:
实际上,是由于运算符的重构所导致的结果。当a被重新赋值时,.NET为a在托管堆上重新分配了一块内存。这样做的目的是,使字符串类型与通俗意义上讲的字符串更接地气。
引用类型数组:
数组元素为值类型时,在托管堆中一次性分配全部值类型空间(堆中栈),并自动初始化;
元素为 引用类型时,先在托管堆分配一次空间,此时不会自动初始化任何元素(均为null)。等到有代码初始化某个元素的时,这个引用类型元素的存储空间才会被分配在托管堆上;
6、程序设置当前项目工作目录
Directory.SetCurrentDirectory(Path.GetDirectoryName(typeof(Test).Assembly.Location));
7、获取App.config配置文件中的值
1、获取appSettings节点值:
ConfigurationManager.AppSettings[key];
2、获取connectionStrings节点值:
var list= ConfigurationManager.ConnectionStrings;
string str="";
foreach (ConnectionStringSettings item in list)
{
if(item.Name=="ConTest")
str = item.ConnectionString;
}
8、Linq常用方法
Where查询(方式1:from in;方式2:Lambda表达式):
List<string> list = new List<string>() { "111","222","33","44"};
var list2=from m in list where m.Length==3 select m;
var list3=list.Where(m => m.Length==2); //使用Lambda表达式
foreach (var m in list3)
Console.WriteLine(m);
order排序:1、descending 降序;2、ascending 升序
List<int> list = new List<int>() { 11, 2, 23, 7 };
//var list2=from m in list orderby m descending select m;
var list2 = from m in list orderby m ascending select m;
//var list3 = list.OrderBy(x => x).ToList();//Lambda表达式
var list3 = list.OrderByDescending(x => x).ToList();
foreach (var m in list3)
Console.WriteLine(m);
四、版本新增
C#9新增顶级语句;
字符串的范围除SubString方法,C#8新增hat(^)、范围运算符([..]);
StringBuilder sb = new StringBuilder("111111");//动态字符串默认容量16字符,成倍增加
sb.Append("abcdefghjklmnopq");
Console.WriteLine(sb.Length);//获取有效长度
Console.WriteLine(sb.Capacity);//获取容量
string rangstr ="hello,auston!" ;
Console.WriteLine(rangstr[..5]);//范围运算符
Console.WriteLine(rangstr[7^2]);//hat^运算符,从索引7往前数第2个字符
string? s=null;
string s2 = s?.ToLower()??string.Format("");//使用null条件运算符和空合并运算符
Console.WriteLine($@"\t{s2}\t{{}}");//字符串插值(带$前缀)与verbatim字符串(带@前缀)
DateTime t = DateTime.Now;
Console.WriteLine($"{t:D}");//字符串格式控制
int a = 2_2_2;//使用数字分隔符,提高代码可读性(编译器会忽略下划线)
Console.WriteLine($"{a:c}");
double d = 22.336_6;
Console.WriteLine($"{d:###.##}");//小数点后四舍五入保留2位
Console.WriteLine($"{d:000.00}");//小数点前保留3位,后保留2位
FormattableString str = $"time:{t},number:{a}";//格式化字符串类型
Console.WriteLine("格式str:{0}",str.Format);//获取格式化字符串的原始字符串
for (int i = 0; i < str.ArgumentCount; i++)
{
Console.WriteLine("Arg{0}:{1}", i, str.GetArgument(i));//获取参数值
}
只读字段(readonly修饰的字段 ),在运行时通过构造函数赋值
只读属性,(set被private修饰),使用属性初始化或在构造函数里初始化,如下:
public int Id { get; } = 23;
元组,把多个类型合为一个类型,不需要创建类、结构、记录
var t=(String:"sss",Int32: 32);
t.Int32 = 2;
t.String = "Auston";.Net提供了Tuple<T>旧类型
ValueTuple<T>类型(推荐)有更好的内置支持