福州百度做网站多少钱,电子商务网站建设与维护期末考试,公众号设置下载wordpress,wordpress最大文件大小01、结构体类型Struct
结构体 struct 是一种用户自定义的值类型#xff0c;常用于定义一些简单#xff08;轻量#xff09;的数据结构。对于一些局部使用的数据结构#xff0c;优先使用结构体#xff0c;效率要高很多。
可以有构造函数#xff0c;也可以没有。因此初始…
01、结构体类型Struct
结构体 struct 是一种用户自定义的值类型常用于定义一些简单轻量的数据结构。对于一些局部使用的数据结构优先使用结构体效率要高很多。
可以有构造函数也可以没有。因此初始化时可以new也可以用默认default。但当给字段设置了初始值时则必须有显示的构造函数。结构体中可以定义字段、属性、方法不能使用终结器。结构体可继承接口并实现接口但不能继承其他类、结构体。结构体是值类型被分配在栈上面因此在参数传递时为值传递。 ⁉️结构体始终都是分配在栈上吗—— 不一定当结构体是类的成员时则会随对象一起分配在堆上。同时当结构体上有引用类型字段时该字段只存储引用对象的地址引用对象还是分配在堆上。 void Main()
{Point p1 default;//Point p1 default(Point);Point p2 new Point(1, 2);p1.X 100;p2.X 100;
}
public struct Point
{public int X;public int Y;public Point(int x, int y){X x;Y y;}
}1.1、只读结构体与只读函数
readonly struct申明一个只读的结构体其所有字段、属性都必须是只读的。
public readonly struct Point
{public readonly int X,Y;
}用在方法上该方法中不可修改任何字段值。这只能用在结构体中结构体不能继承不知道这个特性有什么用
public struct Point
{public int X;public int Y;public readonly int GetValue(){X--; //Error:不可修改return X Y;}
}1.2、Ref 结构体
ref 结构类型 用ref struct申明该结构体只能存储在栈上因此任何会导致其分配到堆上的行为都不支持如装箱、拆箱作为类的成员等都不支持。 Ref 结构体 可用于一些高性能场景System.Span、ReadOnlySpan 都是 readonly ref struct结构体。
public ref struct Point
{public int X,Y;
}02、枚举Enum
枚举类型 是由基础值类型byte、int、long等组成的一组命名常量的值类型用enum来申明定义。常用于一些有固定值的类别申明如性别、方向、数据类型等。
枚举成员默认是int可以修改为其他整数类型如byte、short、uint、long等。枚举项可设置值也可省略或者部分设置值。值默认是从0开始并按顺序依次递增。枚举变量的默认值始终是0。枚举本质上就是命名常量因此可以与值类型进行相互转换强制转换。特性Description常用来定义枚项在UI上的显示内容使用反射获取。
public enum UserType : int //常量类型可以修改为其他整数类型
{[Description(普通会员)]Default,VIP 10,SupperVIP, //继续前一个值为11
}
void Main()
{var t1 UserType.Default;Console.WriteLine(t1.ToString()); //输出名称DefaultConsole.WriteLine((int)t1); //输出值0Console.WriteLine(${t1:F}); //输出名称DefaultConsole.WriteLine(${t1:D}); //输出值0var t2 (UserType)0;int t3 (int)UserType.Default;Console.WriteLine(t1 t2); //True
}2.1、Enum 类API
System.Enum 类型是所有枚举类型的抽象基类提供了一些API方法用于枚举的操作基本都是静态方法。Enum 类型还可以作为泛型约束使用。
静态成员说明HasFlag(Enum)判断位域枚举是否包含一个枚举值返回bool静态成员说明GetNameTEnum(TEnum)获取枚举值的常数名称GetNamesTEnum()获取枚举定义的所有常数名称数组GetValuesTEnum()获取枚举定义的所有成员数组IsDefined(Type, Object)判断给定的值数值或名称是否在枚举中定义ParseTEnum(String)解析数值、名称为枚举转换失败抛出异常TryParseTEnum(String, TEnum)安全的转换同上转换结果通过out参数输出返回bool表示是否转换成功其他说明Type.IsEnumType的属性用于判断一个类型是否枚举类型
2.2、位域Flags
枚举位域用[Flags]特性标记从而可以使用枚举的位操作实现多个枚举值合并的的能力。在有些多选值的场景很有用用一个数值可表示多个内容如QQ的各种钻绿钻、红钻、黄钻…用一个值就可以表示参考下面代码示例。
枚举定义时加上特性[Flags]。要求枚举值必须是2的n次方主要是各个成员的二进制值的对应位都不能一样才能保障按位与、按位或运算的正确。合并值用按位或|判断是否包含可以用按位与或者方法HasFlag(e)。枚举类型命名一般建议用复数名词。
void Main()
{var t1 QQDiamond.Green|QQDiamond.Red; //按位或运算合并多个成员值Console.WriteLine((int)t1); //3同时为绿钻、红钻//判断是否绿钻Console.WriteLine(t1.HasFlag(QQDiamond.Green)); //True//判断是否红钻效果同上Console.WriteLine((t1 QQDiamond.Red) QQDiamond.Red); //True
}[Flags]
public enum QQDiamond : sbyte
{None0b0000, //或者0[Description(绿钻)]Green0b0001, //或者1Red0b0010, //或者2、11Blue0b0100, //或者4、12Yellow0b1000,//或者8、13
}2.3、枚举值转换
枚举值为整形枚举名称为string因此常与int、string进行转换。
转换为枚举说明Enum.Parse()/TryParse()转换枚举值字符串形式、枚举名称为枚举对象支持位域FlgasTEnum(int)强制转换整形值为枚举如果没有不会报错支持位域Flgas
/Parse/TryParse方法解析
var t1 Enum.ParseQQDiamond(3); //Green
var t2 Enum.ParseQQDiamond(Green); //Green
//强转
QQDiamond t3 (QQDiamond)56;枚举转换为string、int说明ToString()获取枚举名称支持位域FlgasEnum.GetName(e)获取枚举名称不支持位域Flgas字符格式G或F获取枚举名称其中F主要用于Flgas枚举强制类型转换(int)TEnum获取枚举值字符格式D或X格式化中获取枚举值D为十进制整形X为16进制
//string
var s1 qd.ToString(); //Green
var s2 Enum.GetName(qd); //Green 不支持位于Flgas
var s3 ${qd:G}; //Green
//int
var n1 (int)qd; //1
var n2 ${qd:D}; //103、日期和时间的故事
在System命名空间中有 下面几个表示日期时间的类型都是不可变的、结构体Struct。
类型说明DateTime常用的日期时间类型默认使用的是本地时间本地时区DateTimeOffset支持时区偏移量的的 DateTime适合跨时区的场景。TimeSpan表示一段时间的时间长度间隔或一天内的时间类似时钟无日期DateOnly 、 TimeOnly.NET 6 引入的只表示日期、时间结构更简单轻量适合特点场景TimeZoneInfo时区可表示世界上的任何时区 Ticks 上面几个时间对象中都有一个 Ticks值其值为从公元0001/01/01开始的计数周期。1 Tick 一个周期为100纳秒ns0.1微秒(us)千万分之一秒可以看做是C#中的最小时间单位。 Console.WriteLine(DateTime.Now.Ticks); //638504277997063647
Console.WriteLine(DateTimeOffset.Now.Ticks); //638504277997063874
Console.WriteLine(TimeSpan.FromSeconds(1).Ticks); //100000003.1、什么是UTC、GMT
UTCCoordinated Universal Time世界标准时间协调时间时间简单理解就是 0时区的时间是国际通用时间。它与0度经线的平太阳时相差不超过1秒接近格林尼治标准时间GMT。
**格林尼治标准时间Greenwich Mean TimeGMT**是指位于伦敦郊区的皇家格林尼治天文台的标准时间因为本初子午线被定义在通过那里的经线。 理论上来说格林尼治标准时间的正午是指当太阳横穿格林尼治子午线时的时间。 由于地球在它的椭圆轨道里的运动速度不均匀因此GMT是不稳定的。而UTC时间是由原子钟提供的更为精确可靠基本上已经取代GMT标准了。 我们日常使用的DateTime.Now获取的时间其实是带了本地时区的TimeZone北京时区8小时就是相比UTC时间多了8个小时的偏差时差。DateTime 的Kind属性为DateTimeKind枚举指定了时区类型
Unspecified不确定的大部分场景会被认为是Local的。UtcUTC标准时区偏移量为0。Local默认值本地时区的时间偏移量根据本地时区计算如北京时间的偏移量为8小时。
public enum DateTimeKind
{Unspecified,Utc,Local
}3.2、DateTime
静态成员说明Now、UtcNow当前本地时间、当前UTC时间还有一个Today 只有日期部分的值MinValue、MaxValue最小、最大值UnixEpochUnix 0点的时间值就是 1970 年 1 月 1 日的 00:00:00.0000000 UTCParse、ParseExact解析字符串转换为DateTime值转换失败会抛出异常TryParse、TryParseExact作用同上安全版本的Exact版本的方法可配置时间字符格式实例成员说明Date只有日期部分的DateTime值KindDateTimeKind 类型默认Local构造函数中可以指定Ticks计时周期总数单位为100ns纳秒Year、Month、Day…当前时间的年、月、日、星期等等方法Add***添加值后返回一个新的 DateTime可以为负数ToString(String)转换为字符串指定日期时间格式详细格式参考《String字符串全面了解》ToUniversalTime()转换为UTC时间
3.3、DateTimeOffset
DateTimeOffset 和 DataTime 很像使用、构造方式、API都差不多。主要的区别就是多了时区偏移Offset构造函数中可以用 TimeSpan 指定偏移量。DateTimeOffset 内部有两个比较重要的字段
用一个短整型 short _offsetMinutes 来存储时区偏移量基于UTC单位为分钟。用一个DateTime 存储始终为UTC的日期时间。
静态成员说明UtcTicks(UTC) 日期和时间的计时周期数Offset时区偏移量如北京时间DateTimeOffset.Now.Offset //08:00:00UtcDateTime返回本地UTC的DateTimeLocalDateTime返回本地时区的DateTimeDateTime返回Kind类型为Unspecified的DateTime忽略了时区的DateTime值 用一个示例来理解DataTime、DataTimeOffset的区别 比如你在一个跨国跨时区团队你要发布一个通知 “本周五下午5点前提交周报”不同时区都是周五下午5点前提交报告虽然他们不是同一时刻此时可用DateTime。“明天下午5点开视频会”此时则需要大家都在同一时刻上线远程会议可能有些地方的是白天有些则在黑夜此时可用DateTimeOffset。 3.4、TimeSpan
TimeSpan 用来表示一段时间长度最大值为1000W天最小值为100纳秒。常用TimeSpan.From***()、构造函数、或DateTime的 差值结果 来构造。
TimeSpan t1 TimeSpan.FromSeconds(12); //00:00:12 //12秒
TimeSpan t2 new TimeSpan(12,0,0) - t1; //11:59:48 //11小时59分48秒
TimeSpan t3 DateTime.Now.AddSeconds(12) - DateTime.Now; 00:00:12
var t4 new TimeSpan(15,1,0,0); //15.01:00:00 //15天1小时
var t5 DateTime.Now.TimeOfDay; //当天的时间04、record是什么类型
record 记录类型用来定义一个简单的、不可变只读 的数据结构定义比较方便常用于一些简单的数据传输场景。record 本质上就是定义一个class类型也可申明为record struct结构体因此语法上就是 类型申明主构造函数的形式。 可以把 Record 看做是一个快速定义类结构体的语法糖编译器会构建完整的类型。 构造函数中的参数会生成公共的只读属性其他自动生成的内容还包括Equals、ToString、解构赋值等。record 默认为class可缺省用record struct 则可申明为一个结构体的。record 类型可以继承另一个record类型或接口但不能继承其他普通class。支持使用with语句创建非破坏性副本。
public record Car(string Width); //class
public record struct User(string Name, int Age);//struct
public record class Person(DateTime Birthday); //class
void Main()
{var u1 new User(sam,122);var u2 new User(sam,122);u1.Age 1; //只读不可修改Console.WriteLine(u1 u2); //TrueConsole.WriteLine(Object.ReferenceEquals(u1,u2)); //Falsevar (name,_) u1; //解构赋值Console.WriteLine(name); //sam
}
public record Person2 //创建一个可更改的recored类型
{public string FirstName { get; set; }public string LastName { get; set; }
};通过查看编译后的代码来了解recored的本质下面是代码public record User(string Name, int Age)编译后生成的代码简化后完整代码可查看在线 sharplab代码。
主构造函数中的参数都生成了只读属性如果是struct结构体则属性是可读、可写的。生成了ToString() 方法用stringBuilder 打印了所有字段名、字段值。生成了相等比较的方法、相等运算符重载及GetHashCode()相等比较会比较字段值。还生成了Deconstruct方法用来支持解构赋值var (name,age) new User(sam,19);。
public class User : IEquatableUser
{public string Name{get;init;}public int Age{get;init;}public User(string Name, int Age){this.Name Name;this.Age Age;}public override string ToString(){StringBuilder stringBuilder new StringBuilder();//把所有字段名、值输出return stringBuilder.ToString();}public static bool operator !(User left, User right){return !(left right);}public static bool operator (User left, User right){...}public override int GetHashCode(){...}public virtual bool Equals(User other){...}//支持解构赋值Deconstructpublic void Deconstruct(out string Name, out int Age){Name this.Name;Age this.Age;}}record 申明可以用简化的语法只有主构造函数没有“身体”也可以和class一样自定义一些内部成员。如下面示例中自定义实现了ToString方法则编译器就不会再生成该方法了同时这里加了密封sealed标记子类也就不能重写了。
void Main()
{var u new User(John, 25);Console.WriteLine(u.ToString());u.SayHi();
}public record User(string Name, int Age)
{public sealed override string ToString() ${Name} {Age}; public void SayHi() Console.WriteLine($Hi {Name});
}05、元祖Tuple
元祖 Tuple 其实就微软内置的一组包含若干个属性的泛型类型包括结构体类型的 System.ValueTuple、引用类型的 System.Tuple包含1到8个只读属性。
System.ValueTuple是值类型结构体成员是字段可修改。System.Tuple 类型是引用类型成员是只读属性。 优先推荐使用 ValueTuple这也是微软深度支持的性能更好默认类型推断用的都是ValueTuple。Tuple 作为历史的产物在语言级别没有任何特殊支持。 下面代码为TupleT1的源代码就是这么朴实无华其他就是相等比较、ToString、索引器。
public struct ValueTupleT1, T2
{public T1 Item1;public T2 Item2;public ValueTuple(T1 item1, T2 item2){Item1 item1;Item2 item2;}
}C#在语法层面对ValueTuple的操作提供了很多便捷支持让元祖的使用非常简单、优雅基本可以替代匿名类型。 简化 Tuplec 申明用括号的简化语法(Type,Type,...)(string,int)等效于ValueTuplestring,int编译器会进行类型推断。值相等元祖内部实现了相等比较操作符重载比较的是字段值。元素命名元祖可以显示指定字段名称比原来的无意义Item1、Item2好用多了。不过命名是开发态支持编译后还是Item1、Item2因此在运行时反射不可用。解构赋值元祖对解构的支持是编译器行为。
ValueTupledouble,double p1 new (1,5);
//简化语法
(double, double) p2 (3, 5.5);
var p3 (3, 5.5); //类型推断进一步简化
var dis p2.Item1 * p2.Item2; //Item1、Item2 成员
//值比较
Console.WriteLine(p2 p3); //True
//命名有名字的元祖
var p4 (Name:sam,Age:22);
Console.WriteLine(p4.Name); //sam
//解构赋值
var (n,age) p4;
Console.WriteLine(n); //sam元祖的一个比较适用场景就是方法返回多个值虽然本质上还是一个“值”。
void Main()
{var u FindUser(1);var (nn,ss) FindUser(2);Console.WriteLine(u.nameu.score);Console.WriteLine(nnss);
}public (string name,int score) FindUser(int id) //返回一个元祖
{return (sam,1000);
}06、匿名类型Class
匿名类型就是无需事先申明可直接创建任意实例的一种类型。使用 new {}语法创建创建时申明字段并赋值。
由编译器进行推断创建出一个完整类型。匿名类型属性都是只读的同时实现了相等比较、ToString()方法。
var u new { Name same, Age 10, Birthday DateTime.Now };
Console.WriteLine(u.Name);
//u.Age120; //只读不可修改因此匿名类型也是一种语法糖由编译器来生成完整的类型。大多数场景都可以由 ValueTuple 代替性能更好也不需要额外的类型了。 07、其他内置类型
7.1、Console
Console 静态类控制台输入、输出。
成员说明BackgroundColor获取、设置控制台背景色ForegroundColor获取、设置控制台前景色WriteLine(String)输出内容到控制台ReadLine()接受控制台输入Beep()播放一个提示音参数还可以设置播放时长Clear()清空控制台
7.2、Environment
Environment 静态类提供全局环境的一些参数和方法算是比较常用了。
成员说明CurrentDirectory当前程序的工作目录是运行态可变的不一定是exe目录ProcessPath当前程序exe的地址.NET 5支持CurrentManagedThreadId当前托管现线程的IDIs64BitOperatingSystem获取操作系统是否64位Is64BitProcess 获取当前进程是否64位进程。NewLine换行符\\r\\nOSVersion获取操作系统信息ProcessId获取当前进程IDProcessorCount获取CPU处理器核心数UserName获取当前操作系统的用户名WorkingSet获取当前进程的物理内存量Exit(Int32)退出进程GetFolderPath(SpecialFolder)获取系统特定文件夹目录如临时目录、桌面等SetEnvironmentVariable设置环境变量
7.2、AppDomain、AppContext
AppDomain 是.Net Framework时代的产物用来表示一个应用程序域进程中可以创建多个引用程序域拥有独立的程序集、隔离环境。在.Net Core 中 其功能大大削弱了不再支持创建AppDomain就只有一个CurrentDomain了。AppContext 表示全局应用上下文对象是一个静态类。.NET Core引入的新类可用来存放一些全局的数据、开关API比较少。 AppDomain成员说明CurrentDomain静态属性获取当前应AppDomainBaseDirectory ⭐获取程序跟目录Load(AssemblyName)加载程序集AssemblyUnhandledException ⭐全局未处理异常 事件可用来捕获处理全局异常
AppContext成员说明BaseDirectory获取程序跟目录⭐TargetFrameworkName获取当前.Net框架版本GetData(String)获取指定名称的对象数据SetData 设置数据。TryGetSwitch(String, Boolean)获取指定名称的bool值数据SetSwitch 设置数据。 参考资料
.NET类型系统①基础C# 文档日期、时间和时区《C#8.0 In a Nutshell》