1 基础
1.1 数据类型
00.总结
a.说明
数据类型主要用于指明变量和常量存储值的类型,C# 语言是一种强类型语言,要求每个变量都必须指定数据类型。
b.分类
C# 语言的数据类型分为值类型和引用类型。
值类型包括整型、浮点型、字符型、布尔型、枚举型、结构体
引用类型包括类、接口、数组、委托、字符串等
c.区别
值类型:值是存放到栈中的,每次存取值都会在该内存中操作,赋值时直接复制数据
引用类型:首先会在栈中创建一个引用变量,然后在堆中创建对象本身,再把这个对象所在内存的首地址赋给引用变量
d.总结
类型 存储位置 典型示例 特点
值类型 栈 int、float、bool 存储数据本身,独立副本
引用类型 堆 string、class、array 存储对象地址,多个引用指向同一对象
指针类型 自定义 int*、char* 不安全代码,直接内存操作
e.值类型
| 类型 | 示例 | 描述
|-------|------------------------|----------------------------
| 整型 | int、long | 表示整数,有符号和无符号之分
| 浮点型 | float、double、decimal | 表示带小数的数值,精度不同,适用于科学计算和金融计算
| 字符型 | char | 表示单个 Unicode 字符
| 布尔型 | bool | 表示布尔值 true 或 false
| 枚举型 | enum | 表示一组命名的整型常量,便于代码的可读性
| 结构体 | struct | 自定义值类型,包含多个字段,可存储复杂数据结构
f.引用类型
| 引用类型 | 描述
|----------|----------------------------
| 类 | 使用 class 定义的自定义类型或内置类
| 接口 | 使用 interface 定义的一组行为规范
| 数组 | 包含元素的集合,例如 int[]、string[]
| 字符串 | 表示文本数据的 string 类型
| 委托 | 表示方法引用的类型,用于事件和回调
| 动态类型 | dynamic 类型,编译时跳过类型检查
| 对象 | object,所有类型的基类
| 可空引用 | 引入 C# 8 的空引用类型,允许显式 null 值
g.指针类型
| 指针类型 | 描述
|---------|-------------
| int* | 指向整数的指针
| char* | 指向字符的指针
| float* | 指向浮点数的指针
| double* | 指向双精度浮点数的指针
| void* | 通用指针
01.值类型(Value types)
a.整型
a.有符号整型
int:32 位整数,范围为 -2,147,483,648 到 2,147,483,647。
short:16 位整数,范围为 -32,768 到 32,767。
long:64 位整数,范围为 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。
sbyte:8 位整数,范围为 -128 到 127。
b.无符号整型
uint:32 位无符号整数,范围为 0 到 4,294,967,295。
ushort:16 位无符号整数,范围为 0 到 65,535。
ulong:64 位无符号整数,范围为 0 到 18,446,744,073,709,551,615。
byte:8 位无符号整数,范围为 0 到 255。
b.浮点型
a.单精度
float:32 位浮点数,有效数字大约为 7 位,适合对精度要求不高的数据。
b.双精度
double:64 位浮点数,有效数字大约为 15-16 位,是 C# 中默认的浮点类型,适合大多数科学计算。
c.十进制
decimal:128 位十进制浮点数,有效数字大约为 28-29 位,适合金融计算,能精确表示小数。
c.字符型
字符型用于表示单个 Unicode 字符,使用 char 类型。
char:16 位 Unicode 字符,范围为 \u0000 到 \uFFFF。可用于存储单个字符,如 'A'、'9' 等。
d.布尔型
布尔型用于表示真或假值。
bool:只能取值 true 或 false,通常用于条件判断和逻辑控制
e.枚举型
枚举类型是一个值类型的集合,用于定义一组命名常量。通常使用 enum 关键字定义。
enum:定义了一组整数常量,默认从 0 开始计数。例如,enum Days { Sunday, Monday, Tuesday } 定义了 Days 枚举,Sunday 对应 0,Monday 对应 1,以此类推。
枚举可与 int、byte 等整数类型关联,因此每个枚举项都是一个具体的整型数值。
f.结构体
结构体是一种自定义的值类型,用于将不同类型的数据组合在一起。常用于封装多个数据字段的组合。
struct:包含多个字段和方法,类似类的定义,但它是值类型,使用时会创建其副本。
常见结构体包括 DateTime(表示日期和时间)、TimeSpan(表示时间间隔)。
02.引用类型(Reference types)
a.类(Class)
自定义类:使用 class 关键字定义的类型,比如 class Person { ... }。
类可以包含字段、属性、方法和事件等,实例化时在堆中分配内存。
内置类:如 System.Object(所有类型的基类)、System.String、System.Exception 等。
-----------------------------------------------------------------------------------------------------
public class Person
{
public string Name { get; set; }
}
b.接口(Interface)
接口定义了一组行为约定,类和结构可以实现接口来定义具体行为。接口是引用类型,即使它的实现类是值类型。
-----------------------------------------------------------------------------------------------------
public interface IAnimal
{
void Speak();
}
c.数组(Array)
所有数组类型在 C# 中都是引用类型,包括 int[]、string[] 等,无论存储的元素是值类型还是引用类型。
-----------------------------------------------------------------------------------------------------
int[] numbers = { 1, 2, 3, 4 };
d.字符串(String)
string 是一个不可变的引用类型,用于表示文本数据。字符串的操作会创建新的字符串对象,而不会修改原始对象。
-----------------------------------------------------------------------------------------------------
string greeting = "Hello, World!";
e.委托(Delegate)
委托是引用类型,用于定义对方法的引用。委托是 C# 中实现事件和回调机制的基础。
-----------------------------------------------------------------------------------------------------
public delegate void Notify(string message);
f.动态类型(Dynamic)
dynamic 是一种在编译时跳过类型检查的引用类型,允许在运行时动态解析类型。
尽管动态类型可以赋值给任何类型,但它仍然是引用类型。
-----------------------------------------------------------------------------------------------------
dynamic obj = "Hello, World!";
g.Object 类型(object)
object 是 C# 中所有类型的基类,值类型和引用类型都可以视为 object 类型。它是最基本的引用类型。
-----------------------------------------------------------------------------------------------------
object number = 123; // int 被装箱为 object
h.Null 类型(Nullable)
空引用类型:默认情况下,引用类型可以赋值为 null,表示不引用任何对象。
C# 8.0 开始支持可空引用类型,可用 ? 标记,如 string?。
-----------------------------------------------------------------------------------------------------
string? nullableString = null; // 可空引用类型
03.指针类型(Pointer types)
a.介绍
在 C# 中,指针类型(Pointer Types)可以在不安全代码块中使用,它们允许直接访问内存地址。
指针类型主要用于高性能的底层代码,例如与操作系统或硬件交互时。
b.指针类型的定义
指针类型使用 * 符号定义,其格式为:<type>*,其中 <type> 是基础数据类型。
指针指向特定类型的内存地址。例如,int* 表示一个指向整数的指针。
-----------------------------------------------------------------------------------------------------
常用的指针类型包括:
int*:指向整数的指针。
char*:指向字符的指针。
float*:指向浮点数的指针。
double*:指向双精度浮点数的指针。
void*:通用指针,类似 C 中的 void*。
c.指针操作
在指针类型中,常见操作包括:
取地址:使用 & 操作符获取变量的地址。
解引用:使用 * 操作符访问指针指向的变量。
指针运算:可以对指针进行加减操作(如 ptr + 1),用于访问数组中的其他元素。
d.不安全代码块
C# 中的指针只能在 unsafe(不安全)代码块中使用,并且编译时需启用不安全代码的编译选项。以下是一个指针类型的示例:
using System;
namespace UnsafeCodeExample
{
class Program
{
static unsafe void Main(string[] args)
{
int num = 10;
int* ptr = # // 获取 num 的地址
Console.WriteLine("num 的值: " + num);
Console.WriteLine("指针 ptr 指向的值: " + *ptr); // 解引用,输出 num 的值
}
}
}
e.指针类型的注意事项
不安全代码:指针类型只能在 unsafe 环境中使用,通常需要在项目设置中启用不安全代码。
垃圾回收:指针类型的使用会绕过 C# 的垃圾回收机制,因此需要谨慎管理内存,以避免内存泄漏。
不支持指针的类型:指针不能用于引用类型(如类),只能用于值类型(如 int、char、double 等)。
f.指针类型的用途
指针类型通常用于以下场景:
与底层系统 API 的交互,例如调用非托管代码或 Win32 API。
高性能应用程序,尤其是需要直接内存访问的场合。
实现特定的低级别操作,如操作字节数据流或处理图像数据。
04.关键字
a.说明
关键字是 C# 编译器预定义的保留字。这些关键字不能用作标识符,但是,如果您想使用这些关键字作为标识符,可以在关键字前面加上 @ 字符作为前缀。
在 C# 中,有些关键字在代码的上下文中有特殊的意义,如 get 和 set,这些被称为上下文关键字(contextual keywords)。
下表列出了 C# 中的保留关键字(Reserved Keywords)和上下文关键字(Contextual Keywords)
b.保留关键字
| abstract | as | base | bool | break | byte | case |
|-----------|-----------|----------|------------|--------|-------------|---------|
| catch | char | checked | class | const | continue | decimal |
| default | delegate | do | double | else | enum | event |
| explicit | extern | false | finally | fixed | float | for |
| foreach | goto | if | implicit | in | in (generic | int |
| interface | internal | is | lock | long | namespace | new |
| null | object | operator | out | out | override | params |
| private | protected | public | readonly | ref | return | sbyte |
| sealed | short | sizeof | stackalloc | static | string | struct |
| switch | this | throw | true | try | typeof | uint |
| ulong | unchecked | unsafe | ushort | using | virtual | void |
| volatile | while |
c.上下文关键字
| add | alias | ascending | descending | dynamic | from | get |
|----------|--------|-----------|------------|---------|---------|---------|
| global | group | into | join | let | orderby | partial |
| (type) |
| partial |
| (method) | remove | select | set |
05.代码
namespace basicApp;
internal class basic01
{
// private static void Main(string[] args)
// {
// Console.WriteLine("值类型示例:");
// ShowValueTypes();
//
// Console.WriteLine("\n引用类型示例:");
// ShowReferenceTypes();
//
// Console.WriteLine("\n指针类型示例 (仅在不安全代码块中运行):");
// ShowPointerTypes();
// }
// 方法:演示值类型
private static void ShowValueTypes()
{
// 整型示例
var integerExample = 100;
var longExample = 100000L;
Console.WriteLine($"整型:int = {integerExample}, long = {longExample}");
// 浮点型示例
var floatExample = 3.14f;
var doubleExample = 3.14159;
var decimalExample = 123.45m;
Console.WriteLine($"浮点型:float = {floatExample}, double = {doubleExample}, decimal = {decimalExample}");
// 字符型示例
var charExample = 'A';
Console.WriteLine($"字符型:char = {charExample}");
// 布尔型示例
var boolExample = true;
Console.WriteLine($"布尔型:bool = {boolExample}");
// 枚举型示例
var day = DayOfWeek.Monday;
Console.WriteLine($"枚举型:DayOfWeek = {day}");
// 结构体示例
var point = new Point(3, 5);
Console.WriteLine($"结构体:Point = ({point.X}, {point.Y})");
}
// 方法:演示引用类型
private static void ShowReferenceTypes()
{
// 类示例
var person = new Person("Alice");
Console.WriteLine($"类:Person = {person.Name}");
// 接口示例
IAnimal animal = new Dog();
animal.Speak();
// 数组示例
int[] arrayExample = { 1, 2, 3 };
Console.WriteLine("数组:int[] = " + string.Join(", ", arrayExample));
// 字符串示例
var stringExample = "Hello, World!";
Console.WriteLine($"字符串:string = {stringExample}");
// 委托示例
var greet = Greet;
greet("Alice");
// 动态类型示例
dynamic dynamicExample = "This is dynamic";
Console.WriteLine($"动态类型:dynamic = {dynamicExample}");
// 对象示例
object objectExample = 42;
Console.WriteLine($"对象:object = {objectExample}");
// 可空引用类型示例 (C# 8+)
string? nullableString = null;
Console.WriteLine($"可空引用类型:nullable string = {nullableString}");
}
// // 方法:演示指针类型 (必须在 unsafe 代码块中)
private static void ShowPointerTypes()
{
// 由于不能在 safe 上下文中使用 unsafe 构造,因此此方法已被修改为安全上下文
// 为了演示指针类型,我们将使用安全的方式来模拟指针的行为
var num = 10;
Console.WriteLine($"模拟指针类型:int* = {num} (值: {num})");
var character = 'A';
Console.WriteLine($"模拟指针类型:char* = {character} (值: {character})");
var floatNum = 3.14f;
Console.WriteLine($"模拟指针类型:float* = {floatNum} (值: {floatNum})");
var doubleNum = 6.28;
Console.WriteLine($"模拟指针类型:double* = {doubleNum} (值: {doubleNum})");
}
// 辅助方法:用于演示委托的 Greet 方法
private static void Greet(string name)
{
Console.WriteLine($"委托调用:Hello, {name}!");
}
}
// 值类型示例:枚举类型
internal enum DayOfWeek
{
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
}
// 值类型示例:结构体类型
internal struct Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y)
{
X = x;
Y = y;
}
}
// 引用类型示例:类
internal class Person
{
public Person(string name)
{
Name = name;
}
public string Name { get; }
}
// 引用类型示例:接口
internal interface IAnimal
{
void Speak();
}
// 引用类型示例:接口的实现类
internal class Dog : IAnimal
{
public void Speak()
{
Console.WriteLine("接口调用:Dog says Woof!");
}
}
1.2 类型转换
00.对比
a.总结
方法类别 方法 描述
隐式转换 自动完成的转换 无需手动指定,用于安全的类型转换
显式转换 (type)value 使用强制类型转换符 (),适用于数据范围不同情况
Convert 类 Convert.ToInt32 转换为指定类型,抛出异常时确保数据有效性
Parse 方法 double.Parse 转换字符串为数值类型,字符串无效时抛出异常
TryParse 方法 double.TryParse 安全转换字符串为数值,返回是否转换成功的布尔值
b.汇总
| 方法类别 | 方法 | 描述
|----------------------------------------|-------------------------------------------|--------------------------------
| 隐式转换 | 自动进行的转换 | 无需显式指定,通常用于安全的类型转换,如从较小类型到较大类型
| 显式转换(强制转换) | (type)value | 需要显式指定,通常用于可能导致数据丢失或转换失败的情况
| Convert 类方法 | Convert.ToBoolean(value) | 将指定类型转换为 Boolean
| | Convert.ToByte(value) | 将指定类型转换为 Byte
| | Convert.ToChar(value) | 将指定类型转换为 Char
| | Convert.ToDateTime(value) | 将指定类型转换为 DateTime
| | Convert.ToDecimal(value) | 将指定类型转换为 Decimal
| | Convert.ToDouble(value) | 将指定类型转换为 Double
| | Convert.ToInt16(value) | 将指定类型转换为 Int16(短整型)
| | Convert.ToInt32(value) | 将指定类型转换为 Int32(整型)
| | Convert.ToInt64(value) | 将指定类型转换为 Int64(长整型)
| | Convert.ToSByte(value) | 将指定类型转换为 SByte
| | Convert.ToSingle(value) | 将指定类型转换为 Single(单精度浮点型)
| | Convert.ToString(value) | 将指定类型转换为 String
| | Convert.ToUInt16(value) | 将指定类型转换为 UInt16(无符号短整型)
| | Convert.ToUInt32(value) | 将指定类型转换为 UInt32(无符号整型)
| | Convert.ToUInt64(value) | 将指定类型转换为 UInt64(无符号长整型)
| Parse 方法 | Boolean.Parse(string) | 将字符串解析为 Boolean
| | Byte.Parse(string) | 将字符串解析为 Byte
| | Char.Parse(string) | 将字符串解析为 Char
| | DateTime.Parse(string) | 将字符串解析为 DateTime
| | Decimal.Parse(string) | 将字符串解析为 Decimal
| | Double.Parse(string) | 将字符串解析为 Double
| | Int16.Parse(string) | 将字符串解析为 Int16
| | Int32.Parse(string) | 将字符串解析为 Int32
| | Int64.Parse(string) | 将字符串解析为 Int64
| | SByte.Parse(string) | 将字符串解析为 SByte
| | Single.Parse(string) | 将字符串解析为 Single
| | UInt16.Parse(string) | 将字符串解析为 UInt16
| | UInt32.Parse(string) | 将字符串解析为 UInt32
| | UInt64.Parse(string) | 将字符串解析为 UInt64
| TryParse 方法 | Boolean.TryParse(string, out bool) | 尝试将字符串解析为 Boolean,返回布尔值表示是否成功
| | Byte.TryParse(string, out byte) | 尝试将字符串解析为 Byte,返回布尔值表示是否成功
| | Char.TryParse(string, out char) | 尝试将字符串解析为 Char,返回布尔值表示是否成功
| | DateTime.TryParse(string, out DateTime) | 尝试将字符串解析为 DateTime,返回布尔值表示是否成功
| | Decimal.TryParse(string, out decimal) | 尝试将字符串解析为 Decimal,返回布尔值表示是否成功
| | Double.TryParse(string, out double) | 尝试将字符串解析为 Double,返回布尔值表示是否成功
| | Int16.TryParse(string, out short) | 尝试将字符串解析为 Int16,返回布尔值表示是否成功
| | Int32.TryParse(string, out int) | 尝试将字符串解析为 Int32,返回布尔值表示是否成功
| | Int64.TryParse(string, out long) | 尝试将字符串解析为 Int64,返回布尔值表示是否成功
| | SByte.TryParse(string, out sbyte) | 尝试将字符串解析为 SByte,返回布尔值表示是否成功
| | Single.TryParse(string, out float) | 尝试将字符串解析为 Single,返回布尔值表示是否成功
| | UInt16.TryParse(string, out ushort) | 尝试将字符串解析为 UInt16,返回布尔值表示是否成功
| | UInt32.TryParse(string, out uint) | 尝试将字符串解析为 UInt32,返回布尔值表示是否成功
| | UInt64.TryParse(string, out ulong) | 尝试将字符串解析为 UInt64,返回布尔值表示是否成功
01.隐式类型转换
隐式类型转换是由编译器自动完成的,通常用于不会导致数据丢失的情况,
例如从较小的整型转换到较大的整型,或从派生类转换到基类。此类型转换适合没有数据精度或范围损失的情况。
---------------------------------------------------------------------------------------------------------
byte b = 10;
int i = b; // 从 byte 隐式转换到 int
long l = i; // 从 int 隐式转换到 long
float f = 5.75f;
double d = f; // 从 float 隐式转换到 double
02.显式类型转换
显式类型转换(强制类型转换)适用于需要从较大范围类型转换为较小范围类型的情况。
这类转换可能导致数据丢失(例如从 double 到 int 会丢失小数部分),所以需要使用强制转换符 () 显式指定。
---------------------------------------------------------------------------------------------------------
int i = 100;
byte b = (byte)i; // 从 int 强制转换到 byte
double d = 123.45;
int i2 = (int)d; // 从 double 强制转换到 int,丢失小数部分
03.使用 Convert 类进行类型转换
System.Convert 类提供了丰富的静态方法,可以在各种基本数据类型之间转换,确保在进行类型转换时的安全性。
---------------------------------------------------------------------------------------------------------
string str = "123";
int number = Convert.ToInt32(str); // 将字符串转换为整数
double d = Convert.ToDouble("45.67"); // 将字符串转换为 double
bool b = Convert.ToBoolean("true"); // 将字符串转换为 bool
04.使用 Parse 方法
Parse 方法可将字符串转换为对应的数值类型,如果字符串格式不正确将抛出异常。适用于已知格式且能够确保转换成功的场景。
---------------------------------------------------------------------------------------------------------
string str = "123.45";
double d = double.Parse(str); // 转换成功,d 为 123.45
05.使用 TryParse 方法
TryParse 方法在转换失败时不会抛出异常,而是返回 false,适用于需要安全转换但不确定字符串是否有效的情况。
---------------------------------------------------------------------------------------------------------
string str = "123.45";
double result;
bool success = double.TryParse(str, out result);
if (success) {
Console.WriteLine("转换成功: " + result);
} else {
Console.WriteLine("转换失败");
}
06.自定义类型转换
可以通过定义 implicit 和 explicit 运算符重载来自定义类型的隐式或显式转换。这样可以在类中实现更加灵活的类型转换。
---------------------------------------------------------------------------------------------------------
public class Fahrenheit
{
public double Degrees { get; set; }
public Fahrenheit(double degrees) => Degrees = degrees;
// 隐式转换从 Fahrenheit 到 double
public static implicit operator double(Fahrenheit f) => f.Degrees;
// 显式转换从 double 到 Fahrenheit
public static explicit operator Fahrenheit(double d) => new Fahrenheit(d);
}
// 使用示例
Fahrenheit f = new Fahrenheit(98.6);
double temp = f; // 隐式转换到 double
Fahrenheit newF = (Fahrenheit)temp; // 显式转换回 Fahrenheit
07.代码
namespace basicApp;
internal class basic02
{
// public static void Main(string[] args)
// {
// // 调用不同类型转换示例方法
// ImplicitConversionExample();
// ExplicitConversionExample();
// ConvertClassExample();
// ParseMethodExample();
// TryParseMethodExample();
// CustomTypeConversionExample();
// }
/// <summary>
/// 示例1:隐式类型转换
/// 适用于小范围类型到大范围类型的自动转换
/// </summary>
public static void ImplicitConversionExample()
{
byte b = 10;
int i = b; // 从 byte 隐式转换到 int
long l = i; // 从 int 隐式转换到 long
var f = 5.75f;
double d = f; // 从 float 隐式转换到 double
Console.WriteLine("隐式转换示例:");
Console.WriteLine($"byte -> int: {i}");
Console.WriteLine($"int -> long: {l}");
Console.WriteLine($"float -> double: {d}");
Console.WriteLine();
}
/// <summary>
/// 示例2:显式类型转换
/// 适用于大范围类型到小范围类型的手动转换,可能丢失数据
/// </summary>
public static void ExplicitConversionExample()
{
var i = 100;
var b = (byte)i; // 从 int 强制转换到 byte
var d = 123.45;
var i2 = (int)d; // 从 double 强制转换到 int,丢失小数部分
Console.WriteLine("显式转换示例:");
Console.WriteLine($"int -> byte: {b}");
Console.WriteLine($"double -> int (小数丢失): {i2}");
Console.WriteLine();
}
/// <summary>
/// 示例3:使用 Convert 类进行类型转换
/// 使用 Convert.ToInt32, ToDouble 等方法转换基本数据类型
/// </summary>
public static void ConvertClassExample()
{
var str = "123";
var number = Convert.ToInt32(str); // 将字符串转换为整数
var d = Convert.ToDouble("45.67"); // 将字符串转换为 double
var b = Convert.ToBoolean("true"); // 将字符串转换为 bool
Console.WriteLine("使用 Convert 类进行类型转换示例:");
Console.WriteLine($"字符串 -> int: {number}");
Console.WriteLine($"字符串 -> double: {d}");
Console.WriteLine($"字符串 -> bool: {b}");
Console.WriteLine();
}
/// <summary>
/// 示例4:使用 Parse 方法进行类型转换
/// 使用 Parse 方法转换字符串,转换失败会抛出异常
/// </summary>
public static void ParseMethodExample()
{
try
{
var str = "123.45";
var d = double.Parse(str); // 转换成功
Console.WriteLine("使用 Parse 方法进行类型转换示例:");
Console.WriteLine($"字符串 -> double: {d}");
Console.WriteLine();
}
catch (FormatException)
{
Console.WriteLine("Parse 转换失败: 格式错误");
}
}
/// <summary>
/// 示例5:使用 TryParse 方法进行类型转换
/// 使用 TryParse 方法在转换失败时不会抛出异常
/// </summary>
public static void TryParseMethodExample()
{
var str = "123.45";
if (double.TryParse(str, out var result))
{
Console.WriteLine("使用 TryParse 方法进行类型转换示例:");
Console.WriteLine($"字符串 -> double: {result}");
Console.WriteLine();
}
else
{
Console.WriteLine("TryParse 转换失败");
}
}
/// <summary>
/// 示例6:自定义类型转换
/// 通过定义 implicit 和 explicit 运算符重载实现隐式或显式转换
/// </summary>
public static void CustomTypeConversionExample()
{
var f = new Fahrenheit(98.6);
double temp = f; // 隐式转换到 double
var newF = (Fahrenheit)temp; // 显式转换回 Fahrenheit
Console.WriteLine("自定义类型转换示例:");
Console.WriteLine($"Fahrenheit 对象 -> double: {temp}");
Console.WriteLine($"double -> Fahrenheit 对象: {newF.Degrees}");
Console.WriteLine();
}
}
/// <summary>
/// 自定义温度类 Fahrenheit,支持隐式和显式类型转换
/// </summary>
public class Fahrenheit
{
public Fahrenheit(double degrees)
{
Degrees = degrees;
}
public double Degrees { get; set; }
// 隐式转换从 Fahrenheit 到 double
public static implicit operator double(Fahrenheit f)
{
return f.Degrees;
}
// 显式转换从 double 到 Fahrenheit
public static explicit operator Fahrenheit(double d)
{
return new Fahrenheit(d);
}
}
1.3 变量和常量
01.变量
a.变量定义和初始化
在 C# 中,变量的定义语法如下:
<data_type> <variable_name> = value; // 其中 <data_type> 是变量的数据类型,<variable_name> 是变量名
-----------------------------------------------------------------------------------------------------
例如,定义几个变量并为它们初始化:
int a = 10; // 整型变量
float pi = 3.14f; // 浮点型变量
bool isActive = true; // 布尔型变量
string name = "Alice"; // 字符串变量
b.C# 支持的主要数据类型
类型 例子
整数类型 int, byte, short, long
浮点型 float, double
十进制类型 decimal
布尔类型 bool
字符类型 char
字符串 string
空类型 null, 可空数据类型
c.变量命名规则
变量名可以包含字母、数字和下划线;
变量名必须以字母或下划线开头;
变量名区分大小写,避免使用 C# 的关键字。
d.变量的初始化
变量可以在定义时初始化,也可以在定义后初始化:
-----------------------------------------------------------------------------------------------------
int b;
b = 20; // 初始化
e.示例代码:变量定义、初始化与用户输入
以下示例展示了如何定义和初始化变量,并接收用户的输入值:
using System;
namespace VariableDefinition
{
class Program
{
static void Main(string[] args)
{
// 变量定义和初始化
int a = 10;
int b = 20;
double result = a + b;
// 输出变量值
Console.WriteLine("a = {0}, b = {1}, result = {2}", a, b, result);
// 用户输入
Console.Write("请输入一个整数: ");
int userInput = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("您输入的整数是: {0}", userInput);
}
}
}
f.C# 中的 Lvalues 和 Rvalues
Lvalue:可以出现在赋值语句的左边或右边,表示内存中的一个位置。
Rvalue:只能出现在赋值语句的右边,表示数据值。
示例代码:
int x = 10; // 有效,x 是一个 Lvalue,可以出现在左边
int y = x + 5; // 有效,x 是 Rvalue,可以出现在右边
// 10 = x; // 无效,数值 10 是 Rvalue,不能出现在赋值语句左边
02.常量
a.变量的定义和初始化
使用数据类型定义变量,并根据需要初始化。
变量名需遵循命名规则,避免使用关键字。
变量可以在声明时初始化,如:int a = 10;。
b.常量的定义
常量值在程序运行过程中不可修改,使用 const 关键字定义。
如:const double pi = 3.14159;。
c.变量的作用域
局部变量仅在定义的代码块中有效。
类级别定义的变量(成员变量)在整个类中有效。
静态变量在整个类中可见,但在类实例中是共享的。
d.特殊类型的变量
动态类型(dynamic):在运行时确定数据类型,适用于特殊情况。
字符和字符串常量:char 使用单引号定义单个字符;字符串可以用双引号或 @ 符号定义。
e.示例
using System;
namespace VariableDefinition
{
public class Program
{
// 定义类级常量
public const double PI = 3.14159;
static void Main(string[] args)
{
// 整型变量
int i = 100;
short s = 20;
// 浮点型变量
float f = 12.5f;
double d = PI;
// 布尔型
bool isActive = true;
// 字符和字符串
char letter = 'A';
string message = "Hello, C#!";
// 动态类型
dynamic dynVar = "动态变量";
dynVar = 123; // 运行时确定类型
// 输出变量值
Console.WriteLine("整型变量 i = {0}", i);
Console.WriteLine("短整型变量 s = {0}", s);
Console.WriteLine("浮点型变量 f = {0}, d = {1}", f, d);
Console.WriteLine("布尔变量 isActive = {0}", isActive);
Console.WriteLine("字符 letter = {0}, 字符串 message = {1}", letter, message);
Console.WriteLine("动态变量 dynVar = {0}", dynVar);
// 读取用户输入
Console.Write("请输入一个整数: ");
int userInput = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("您输入的整数是: {0}", userInput);
}
}
}
03.代码
namespace basicApp;
internal class basic03
{
// private static void Main(string[] args)
// {
// // 调用各个方法展示变量的用法
// VariableDefinitionAndInitialization();
// VariableNamingRules();
// VariableInputExample();
// LvaluesAndRvaluesExample();
// staticExample();
// }
/// <summary>
/// 方法1:展示变量的定义与初始化
/// </summary>
private static void VariableDefinitionAndInitialization()
{
// 整型变量的定义和初始化
var a = 10;
var b = 20;
// 浮点型变量的定义和初始化
var pi = 3.14f;
// 布尔变量的定义和初始化
var isActive = true;
// 字符串变量的定义和初始化
var name = "Alice";
// 输出结果
Console.WriteLine("方法1:变量的定义与初始化");
Console.WriteLine("int a = {0}, int b = {1}", a, b);
Console.WriteLine("float pi = {0}", pi);
Console.WriteLine("bool isActive = {0}", isActive);
Console.WriteLine("string name = {0}", name);
Console.WriteLine();
}
/// <summary>
/// 方法2:变量命名规则说明
/// </summary>
private static void VariableNamingRules()
{
// 正确的命名示例
var myVariable = 10;
var _userName = "John";
Console.WriteLine("方法2:变量命名规则");
Console.WriteLine("int myVariable = {0}", myVariable);
Console.WriteLine("string _userName = {0}", _userName);
// 注意:不合法的命名(示例)不能编译
// int 123abc; // 错误:不能以数字开头
// string void; // 错误:不能使用关键字
Console.WriteLine("注意:变量命名应遵循规则,例如:");
Console.WriteLine(" - 变量名必须以字母或下划线开头");
Console.WriteLine(" - 变量名区分大小写,且不应使用 C# 的关键字");
Console.WriteLine();
}
/// <summary>
/// 方法3:接收用户输入并存储到变量中
/// </summary>
private static void VariableInputExample()
{
Console.WriteLine("方法3:接收用户输入");
// 提示用户输入
Console.Write("请输入一个整数: ");
// 使用 Convert.ToInt32() 将输入的字符串转换为整数类型
var userInput = Convert.ToInt32(Console.ReadLine());
// 输出用户输入的值
Console.WriteLine("您输入的整数是: {0}", userInput);
Console.WriteLine();
}
/// <summary>
/// 方法4:展示 Lvalues 和 Rvalues 的使用示例
/// </summary>
private static void LvaluesAndRvaluesExample()
{
Console.WriteLine("方法4:Lvalues 和 Rvalues 示例");
// 有效的 Lvalue 和 Rvalue 示例
var x = 10; // 有效,x 是一个 Lvalue,可以出现在赋值语句的左边
var y = x + 5; // 有效,x 是 Rvalue,可以出现在右边
// 输出结果
Console.WriteLine("int x = {0}; int y = x + 5 = {1}", x, y);
// 无效的 Rvalue 示例:会导致编译错误,故不在代码中执行
// 10 = x; // 无效,10 是 Rvalue,不能出现在赋值语句的左边
Console.WriteLine("注意:Lvalue 可以出现在赋值语句的左边或右边,但 Rvalue 只能在右边。");
Console.WriteLine();
}
/// <summary>
/// 方法5:展示 常量
/// </summary>
private static void staticExample()
{
var PI = 3.14159;
// 整型变量
var i = 100;
short s = 20;
// 浮点型变量
var f = 12.5f;
var d = PI;
// 布尔型
var isActive = true;
// 字符和字符串
var letter = 'A';
var message = "Hello, C#!";
// 动态类型
dynamic dynVar = "动态变量";
dynVar = 123; // 运行时确定类型
// 输出变量值
Console.WriteLine("整型变量 i = {0}", i);
Console.WriteLine("短整型变量 s = {0}", s);
Console.WriteLine("浮点型变量 f = {0}, d = {1}", f, d);
Console.WriteLine("布尔变量 isActive = {0}", isActive);
Console.WriteLine("字符 letter = {0}, 字符串 message = {1}", letter, message);
Console.WriteLine("动态变量 dynVar = {0}", dynVar);
// 读取用户输入
Console.Write("请输入一个整数: ");
var userInput = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("您输入的整数是: {0}", userInput);
}
}
1.4 运算符
01.介绍
a.算术运算符
| 运算符 | 描述 | 实例
|-------|---------------------------------|---------------
| + | 把两个操作数相加 | A + B 将得到 30
| - | 从第一个操作数中减去第二个操作数 | A - B 将得到 -10
| * | 把两个操作数相乘 | A * B 将得到 200
| / | 分子除以分母 | B / A 将得到 2
| % | 取模运算符,整除后的余数 | B % A 将得到 0
| ++ | 自增运算符,整数值增加 1 | A++ 将得到 11
| -- | 自减运算符,整数值减少 1 | A-- 将得到 9
b.关系运算符
| 运算符 | 描述 | 实例
|--------|-----------------------------------------------------------|---------------
| == | 检查两个操作数的值是否相等,如果相等则条件为真。 | (A == B) 不为真。
| != | 检查两个操作数的值是否相等,如果不相等则条件为真。 | (A != B) 为真。
| > | 检查左操作数的值是否大于右操作数的值,如果是则条件为真。 | (A > B) 不为真。
| < | 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 | (A < B) 为真。
| >= | 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 | (A >= B) 不为真。
| <= | 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 | (A <= B) 为真。
c.逻辑运算符
| 运算符 | 描述 | 实例
|--------|-------------------------------------------------------------------------------|---------------
| && | 称为逻辑与运算符。如果两个操作数都非零,则条件为真。 | (A && B) 为假
| || | 称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。 | (A || B) 为真
| ! | 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。 | !(A && B) 为真
d.位运算符
| 运算符 | 描述 | 实例
|--------|-------------------------------------------------------------------------------------|--------------------------------------------
| & | 如果同时存在于两个操作数中,二进制 AND 运算符复制一位到结果中。 | (A & B) 将得到 12,即为 0000 1100
| | | 如果存在于任一操作数中,二进制 OR 运算符复制一位到结果中。 | (A | B) 将得到 61,即为 0011 1101
| ^ | 如果存在于其中一个操作数中但不同时存在于两个操作数中,二进制异或运算符复制一位到结果中。 | (A ^ B) 将得到 49,即为 0011 0001
| ~ | 按位取反运算符是一元运算符,具有"翻转"位效果,即0变成1,1变成0,包括符号位。 | (~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。
| << | 二进制左移运算符。左操作数的值向左移动右操作数指定的位数。 | A << 2 将得到 240,即为 1111 0000
| >> | 二进制右移运算符。左操作数的值向右移动右操作数指定的位数。 | A >> 2 将得到 15,即为 0000 1111
-----------------------------------------------------------------------------------------------------
| p | q | p & q | p | q | p ^ q |
|---|---|-------|-------|-------|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 | 0 |
| 1 | 0 | 0 | 1 | 1 |
e.赋值运算符
| 运算符 | 描述 | 实例
|--------|--------------------------------------------------------------|---------------------------
| = | 简单的赋值运算符,把右边操作数的值赋给左边操作数 | C = A + B 将把 A + B 的值赋给 C
| += | 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 | C += A 相当于 C = C + A
| -= | 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 | C -= A 相当于 C = C - A
| *= | 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 | C *= A 相当于 C = C * A
| /= | 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 | C /= A 相当于 C = C / A
| %= | 求模且赋值运算符,求两个操作数的模赋值给左边操作数 | C %= A 相当于 C = C % A
| <<= | 左移且赋值运算符 | C <<= 2 等同于 C = C << 2
| >>= | 右移且赋值运算符 | C >>= 2 等同于 C = C >> 2
| &= | 按位与且赋值运算符 | C &= 2 等同于 C = C & 2
| ^= | 按位异或且赋值运算符 | C ^= 2 等同于 C = C ^ 2
| |= | 按位或且赋值运算符 | C |= 2 等同于 C = C | 2
f.其他运算符
| 运算符 | 描述 | 实例
|------------------------------------------|--------------------------------------|---------------------------------------------
| sizeof() | 返回数据类型的大小。 | sizeof(int),将返回 4.
| typeof() | 返回 class 的类型。 | typeof(StreamReader);
| & | 返回变量的地址。 | &a; 将得到变量的实际地址。
| * | 变量的指针。 | *a; 将指向一个变量。
| ? : | 条件表达式 | 如果条件为真 ? 则为 X : 否则为 Y
| is | 判断对象是否为某一类型。 | If( Ford is Car) // 检查 Ford 是否是 Car 类的一个对象。
| as | 强制转换,即使转换失败也不会抛出异常。 | Object obj = new StringReader("Hello"); StringReader r = obj as StringReader;
02.代码
namespace basicApp;
internal class basic04
{
// private static void Main(string[] args)
// {
// // 执行各种运算符示例
// ArithmeticOperators();
// RelationalOperators();
// LogicalOperators();
// BitwiseOperators();
// AssignmentOperators();
// ConditionalOperators();
// Console.ReadKey();
// }
/// <summary>
/// 算术运算符示例
/// </summary>
private static void ArithmeticOperators()
{
Console.WriteLine("算术运算符示例:");
var a = 10;
var b = 3;
// 加法
Console.WriteLine($"a + b = {a + b}"); // 输出: 13
// 减法
Console.WriteLine($"a - b = {a - b}"); // 输出: 7
// 乘法
Console.WriteLine($"a * b = {a * b}"); // 输出: 30
// 除法
Console.WriteLine($"a / b = {a / b}"); // 输出: 3
// 取余
Console.WriteLine($"a % b = {a % b}"); // 输出: 1
}
/// <summary>
/// 关系运算符示例
/// </summary>
private static void RelationalOperators()
{
Console.WriteLine("\n关系运算符示例:");
var a = 10;
var b = 5;
// 大于
Console.WriteLine($"a > b: {a > b}"); // 输出: True
// 小于
Console.WriteLine($"a < b: {a < b}"); // 输出: False
// 大于等于
Console.WriteLine($"a >= b: {a >= b}"); // 输出: True
// 小于等于
Console.WriteLine($"a <= b: {a <= b}"); // 输出: False
// 等于
Console.WriteLine($"a == b: {a == b}"); // 输出: False
// 不等于
Console.WriteLine($"a != b: {a != b}"); // 输出: True
}
/// <summary>
/// 逻辑运算符示例
/// </summary>
private static void LogicalOperators()
{
Console.WriteLine("\n逻辑运算符示例:");
var x = true;
var y = false;
// 逻辑与
Console.WriteLine($"x && y: {x && y}"); // 输出: False
// 逻辑或
Console.WriteLine($"x || y: {x || y}"); // 输出: True
// 逻辑非
Console.WriteLine($"!x: {!x}"); // 输出: False
Console.WriteLine($"!y: {!y}"); // 输出: True
}
/// <summary>
/// 按位运算符示例
/// </summary>
private static void BitwiseOperators()
{
Console.WriteLine("\n按位运算符示例:");
var a = 5; // 二进制: 0101
var b = 3; // 二进制: 0011
// 按位与
Console.WriteLine($"a & b: {a & b}"); // 输出: 1
// 按位或
Console.WriteLine($"a | b: {a | b}"); // 输出: 7
// 按位异或
Console.WriteLine($"a ^ b: {a ^ b}"); // 输出: 6
// 按位取反
Console.WriteLine($"~a: {~a}"); // 输出: -6
}
/// <summary>
/// 赋值运算符示例
/// </summary>
private static void AssignmentOperators()
{
Console.WriteLine("\n赋值运算符示例:");
var a = 5;
var b = 3;
// 简单赋值
a = b;
Console.WriteLine($"a = b: {a}"); // 输出: 3
// 加赋值
a += 2;
Console.WriteLine($"a += 2: {a}"); // 输出: 5
// 减赋值
a -= 1;
Console.WriteLine($"a -= 1: {a}"); // 输出: 4
// 乘赋值
a *= 2;
Console.WriteLine($"a *= 2: {a}"); // 输出: 8
// 除赋值
a /= 4;
Console.WriteLine($"a /= 4: {a}"); // 输出: 2
}
/// <summary>
/// 条件运算符示例
/// </summary>
private static void ConditionalOperators()
{
Console.WriteLine("\n条件运算符示例:");
var a = 10;
var b = 20;
// 条件运算符(? :)
var result = a > b ? a : b;
Console.WriteLine($"(a > b) ? a : b: {result}"); // 输出: 20
}
}
1.5 运算符重载
01.介绍
a.概念
您可以重定义或重载 C# 中内置的运算符。因此,程序员也可以使用用户自定义类型的运算符。
重载运算符是具有特殊名称的函数,是通过关键字 operator 后跟运算符的符号来定义的。
与其他函数一样,重载运算符有返回类型和参数列表。
b.说明
例如,请看下面的函数:
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 对象。
c.可重载和不可重载运算符
运算符 描述
+, -, !, ~, ++, -- 这些一元运算符只有一个操作数,且可以被重载。
+, -, *, /, % 这些二元运算符带有两个操作数,且可以被重载。
==, !=, <, >, <=, >= 这些比较运算符可以被重载。
&&, || 这些条件逻辑运算符不能被直接重载。
+=, -=, *=, /=, %= 这些赋值运算符不能被重载。
=, ., ?:, ->, new, is, sizeof, typeof 这些运算符不能被重载。
02.代码
namespace basicApp;
internal class basic05
{
private double breadth; // 宽度
private double height; // 高度
private double length; // 长度
// 获取体积
public double getVolume()
{
return length * breadth * height;
}
// 设置长度
public void setLength(double len)
{
length = len;
}
// 设置宽度
public void setBreadth(double bre)
{
breadth = bre;
}
// 设置高度
public void setHeight(double hei)
{
height = hei;
}
// 重载 + 运算符来将两个 basic05 对象相加
public static basic05 operator +(basic05 b, basic05 c)
{
var box = new basic05();
box.length = b.length + c.length;
box.breadth = b.breadth + c.breadth;
box.height = b.height + c.height;
return box;
}
// 重载 == 运算符
public static bool operator ==(basic05 lhs, basic05 rhs)
{
return lhs.length == rhs.length && lhs.height == rhs.height && lhs.breadth == rhs.breadth;
}
// 重载 != 运算符
public static bool operator !=(basic05 lhs, basic05 rhs)
{
return !(lhs == rhs);
}
// 重载 < 运算符
public static bool operator <(basic05 lhs, basic05 rhs)
{
return lhs.getVolume() < rhs.getVolume();
}
// 重载 > 运算符
public static bool operator >(basic05 lhs, basic05 rhs)
{
return lhs.getVolume() > rhs.getVolume();
}
// 重载 <= 运算符
public static bool operator <=(basic05 lhs, basic05 rhs)
{
return lhs < rhs || lhs == rhs;
}
// 重载 >= 运算符
public static bool operator >=(basic05 lhs, basic05 rhs)
{
return lhs > rhs || lhs == rhs;
}
// 重载 ToString() 方法
public override string ToString()
{
return string.Format("({0}, {1}, {2})", length, breadth, height);
}
}
internal class Tester
{
private static void Main(string[] args)
{
var Box1 = new basic05(); // 声明 Box1,类型为 basic05
var Box2 = new basic05(); // 声明 Box2,类型为 basic05
basic05 Box3; // 声明 Box3,类型为 basic05
basic05 Box4; // 声明 Box4,类型为 basic05
double volume; // 体积
// Box1 详细信息
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// Box2 详细信息
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// 显示 Box1 和 Box2
Console.WriteLine("Box1: {0}", Box1);
Console.WriteLine("Box2: {0}", Box2);
// 计算 Box1 的体积
volume = Box1.getVolume();
Console.WriteLine("Box1 的体积: {0}", volume);
// 计算 Box2 的体积
volume = Box2.getVolume();
Console.WriteLine("Box2 的体积: {0}", volume);
// 将两个 Box 对象相加
Box3 = Box1 + Box2;
Console.WriteLine("Box3: {0}", Box3);
// 计算 Box3 的体积
volume = Box3.getVolume();
Console.WriteLine("Box3 的体积: {0}", volume);
// 比较 Box1 和 Box2
if (Box1 > Box2)
Console.WriteLine("Box1 大于 Box2");
else
Console.WriteLine("Box1 不大于 Box2");
if (Box1 < Box2)
Console.WriteLine("Box1 小于 Box2");
else
Console.WriteLine("Box1 不小于 Box2");
if (Box1 >= Box2)
Console.WriteLine("Box1 大于等于 Box2");
else
Console.WriteLine("Box1 不大于等于 Box2");
if (Box1 <= Box2)
Console.WriteLine("Box1 小于等于 Box2");
else
Console.WriteLine("Box1 不小于等于 Box2");
if (Box1 != Box2)
Console.WriteLine("Box1 不等于 Box2");
else
Console.WriteLine("Box1 等于 Box2");
// Box3 赋值给 Box4
Box4 = Box3;
if (Box3 == Box4)
Console.WriteLine("Box3 等于 Box4");
else
Console.WriteLine("Box3 不等于 Box4");
Console.ReadKey();
}
}
1.6 流程控制
01.介绍
a.判断
| if 语句 | 一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。
|------------------|-----------------------------------------------
| if...else 语句 | 一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行。
| 嵌套 if 语句 | 您可以在一个 if 或 else if 语句内使用另一个 if 或 else if 语句。
| switch 语句 | 一个 switch 语句允许测试一个变量等于多个值时的情况。
| 嵌套 switch 语句 | 您可以在一个 switch 语句内使用另一个 switch 语句。
b.? : 运算符
Exp1 ? Exp2 : Exp3;
c.循环
| 循环类型 | 描述
|--------------------|------------------------------------------
| while 循环 | 当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。
| for/foreach 循环 | 多次执行一个语句序列,简化管理循环变量的代码。
| do...while 循环 | 除了它是在循环主体结尾测试条件外,其他与 while 语句类似。
| 嵌套循环 | 您可以在 while、for 或 do..while 循环内使用一个或多个循环。
d.标签
break语句
continue语句
02.代码
namespace basicApp;
internal class ControlStatements
{
// a. 判断语句
// if语句示例
public void IfStatementExample()
{
var a = 10;
if (a > 5) Console.WriteLine("a 大于 5");
}
// if...else 语句示例
public void IfElseStatementExample()
{
var a = 3;
if (a > 5)
Console.WriteLine("a 大于 5");
else
Console.WriteLine("a 小于等于 5");
}
// 嵌套 if 语句示例
public void NestedIfExample()
{
var a = 10;
var b = 20;
if (a == 10)
if (b == 20)
Console.WriteLine("a 是 10,且 b 是 20");
}
// switch 语句示例
public void SwitchStatementExample()
{
var day = 3;
switch (day)
{
case 1:
Console.WriteLine("今天是星期一");
break;
case 2:
Console.WriteLine("今天是星期二");
break;
case 3:
Console.WriteLine("今天是星期三");
break;
default:
Console.WriteLine("今天是其他日子");
break;
}
}
// 嵌套 switch 语句示例
public void NestedSwitchExample()
{
var a = 10;
var b = 20;
switch (a)
{
case 10:
{
switch (b)
{
case 20:
Console.WriteLine("a 是 10,且 b 是 20");
break;
}
break;
}
default:
Console.WriteLine("没有匹配的情况");
break;
}
}
// b. ? : 条件运算符示例
public void ConditionalOperatorExample()
{
var a = 10;
var result = a > 5 ? "a 大于 5" : "a 小于等于 5";
Console.WriteLine(result);
}
// c. 循环语句
// while 循环示例
public void WhileLoopExample()
{
var i = 0;
while (i < 5)
{
Console.WriteLine("当前 i 的值为:" + i);
i++;
}
}
// for 循环示例
public void ForLoopExample()
{
for (var i = 0; i < 5; i++) Console.WriteLine("当前 i 的值为:" + i);
}
// foreach 循环示例
public void ForEachLoopExample()
{
string[] names = { "张三", "李四", "王五" };
foreach (var name in names) Console.WriteLine("名字:" + name);
}
// do...while 循环示例
public void DoWhileLoopExample()
{
var i = 0;
do
{
Console.WriteLine("当前 i 的值为:" + i);
i++;
} while (i < 5);
}
// 嵌套循环示例
public void NestedLoopExample()
{
for (var i = 1; i <= 3; i++)
for (var j = 1; j <= 3; j++)
Console.WriteLine("i = " + i + ", j = " + j);
}
// d. 标签控制
// break 语句示例
public void BreakStatementExample()
{
for (var i = 0; i < 10; i++)
{
if (i == 5) break; // 当 i 等于 5 时退出循环
Console.WriteLine("i = " + i);
}
}
// continue 语句示例
public void ContinueStatementExample()
{
for (var i = 0; i < 10; i++)
{
if (i % 2 == 0) continue; // 跳过偶数
Console.WriteLine("i = " + i);
}
}
}
internal class basic06
{
private static void Main(string[] args)
{
var cs = new ControlStatements();
// 执行判断语句示例
cs.IfStatementExample();
cs.IfElseStatementExample();
cs.NestedIfExample();
cs.SwitchStatementExample();
cs.NestedSwitchExample();
// 执行条件运算符示例
cs.ConditionalOperatorExample();
// 执行循环语句示例
cs.WhileLoopExample();
cs.ForLoopExample();
cs.ForEachLoopExample();
cs.DoWhileLoopExample();
cs.NestedLoopExample();
// 执行标签控制示例
cs.BreakStatementExample();
cs.ContinueStatementExample();
Console.ReadKey(); // 等待用户按键
}
}
1.7 附:特性
01.特性1
C# 是大小写敏感的。
程序的执行从 Main 方法开始。
所有的语句和表达式必须以分号(;)结尾。
与 Java 不同的是,文件名可以不同于类的名称。
99.示例
a.示例1
ConsoleApp
.git 文件夹
.idea 文件夹
bin 文件夹
obj 文件夹
.gitignore 文件
ConsoleApp.csproj 文件
ConsoleApp.sln 文件
Program.cs 文件
-----------------------------------------------------------------------------------------------------
using System;
namespace HelloWorldApplication
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Console.ReadKey();
}
}
}
-----------------------------------------------------------------------------------------------------
namespace vilgoh.com.ruoyi;
public class Application
{
private static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Console.ReadKey();
}
}
b.示例2
using System;
namespace RectangleApplication
{
class Rectangle
{
// 成员变量
double length;
double width;
public void Acceptdetails()
{
length = 4.5;
width = 3.5;
}
public double GetArea()
{
return length * width;
}
public void Display()
{
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}
class ExecuteRectangle
{
static void Main(string[] args)
{
Rectangle r = new Rectangle();
r.Acceptdetails();
r.Display();
Console.ReadLine();
}
}
}
c.示例3
namespace RectangleApplication;
internal class Rectangle
{
// 成员变量
private double length;
private double width;
public void Acceptdetails()
{
length = 4.5;
width = 3.5;
}
public double GetArea()
{
return length * width;
}
public void Display()
{
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}
internal class ExecuteRectangle
{
private static void Main(string[] args)
{
var r = new Rectangle();
r.Acceptdetails();
r.Display();
Console.ReadLine();
}
}
d.一个 C# 程序主要包括以下部分
命名空间声明(Namespace declaration)
一个 class
Class 方法
Class 属性
一个 Main 方法
语句(Statements)& 表达式(Expressions)
注释
e.说明
程序的第一行 using System; - using 关键字用于在程序中包含 System 命名空间。 一个程序一般有多个 using 语句。using 关键字用于在程序中包含命名空间
下一行是 namespace 声明。一个 namespace 里包含了一系列的类。HelloWorldApplication 命名空间包含了类 Program。
下一行是 class 声明。类 Program 包含了程序使用的数据和方法声明。类一般包含多个方法。方法定义了类的行为。在这里,Program 类只有一个 Main 方法。
下一行定义了 Main 方法,是所有 C# 程序的 入口点。Main 方法说明当执行时 类将做什么动作。
下一行 /*...*/ 将会被编译器忽略,且它会在程序中添加额外的 注释。
Main 方法通过语句 Console.WriteLine("Hello World"); 指定了它的行为。
WriteLine 是一个定义在 System 命名空间中的 Console 类的一个方法。该语句会在屏幕上显示消息 "Hello World"。
最后一行 Console.ReadKey(); 是针对 VS.NET 用户的。这使得程序会等待一个按键的动作,防止程序从 Visual Studio .NET 启动时屏幕会快速运行并关闭。
1.8 附:命令
00.发布为exe
a.方式1:使用 .NET CLI 编译
第1步:确保已安装 .NET SDK: 首先,你需要在系统中安装 .NET SDK,可以通过命令 dotnet --version 来检查。如果没有安装,可以从 .NET官网 下载并安装。
第2步:在项目目录中创建控制台项目: 通过命令行进入 Program.cs 所在的目录,并执行以下命令创建新的控制台应用程序(如果没有创建过):
dotnet new console -o YourProjectName
第3步:编译为 .exe 文件: 在包含 Program.cs 的项目文件夹中执行以下命令进行编译:
dotnet publish -c Release -r win-x64 --self-contained
-c Release 表示发布模式为 Release。
-r win-x64 指定目标平台(例如:win-x64 为 Windows 64位,win-x86 为 Windows 32位)。
--self-contained 表示生成包含所有依赖项的独立程序。
第4步:找到生成的 .exe 文件: 编译完成后,程序会出现在 YourProjectName/bin/Release/netX.X/win-x64/publish 文件夹中,Program.exe 就在该目录下。
b.方式2
第1步:打开 Visual Studio 并创建控制台应用程序: 如果还没有项目,可以在 Visual Studio 中选择“新建项目 > 控制台应用程序”,然后将你的代码粘贴到 Program.cs 文件中。
第2步:编译并生成 .exe 文件:
在“解决方案资源管理器”中右键点击项目名称,选择“发布”选项。
在“发布配置”中,选择发布到文件夹,并指定发布位置。
点击发布后,Visual Studio 会生成相应的 .exe 文件。
第3步:找到生成的 .exe 文件: 编译完成后,可以在发布文件夹中找到生成的 .exe 文件。
c.输出
cd 项目文件夹
dotnet publish -c Release -r win-x64 --self-contained
-----------------------------------------------------------------------------------------------------
<项目文件夹>\bin\Release\net8.0\win-x64\publish\ConsoleApp.exe
1.9 附:dotnet
00.介绍
dotnet-sdk-8.0.403-win-x64.exe并不包含 .NET Framework 框架或 .NET Core 框架。
相反,它是 .NET SDK,用于开发和构建应用程序。它包含用于开发 .NET 应用的工具、库和编译器,
使开发者能够创建、调试和发布 .NET 6、.NET 7 以及最新的 .NET 8 应用。
具体来说,SDK 不包含运行时环境。要运行 .NET Framework 应用或 .NET Core 应用,
仍需分别安装相应的 .NET Runtime 和 .NET Framework(如需要兼容旧版 .NET Framework 的应用)。
简单来说,dotnet-sdk-8.0.403-win-x64.exe 主要用于开发环境,帮助构建基于 .NET 平台的应用,但不包含实际的运行时库。
01.NET SDK
a.下载
https://dotnet.microsoft.com/zh-cn/download/dotnet
选择.NET 8.0
b.环境变量
C:\Program Files\dotnet\
c.验证
dotnet --info
d.命令交互
a.创建的工程名为HelloCsharp
d:
cd D:\software_xare\workspace_sharp
dotnet new console --name HelloCsharp
b.打开项目
cd HelloCsharp
vi Program.cs
Console.WriteLine("Hello, World!");
c.构建运行
dotnet build
dotnet run
d.输出
Hello, World!
1.10 附:路线图
01.介绍
a.概念
C# 是一个现代的、通用的、面向对象的编程语言,它是由微软(Microsoft)开发的,由 Ecma 和 ISO 核准认可的。
C# 是由 Anders Hejlsberg 和他的团队在 .Net 框架开发期间开发的。
C# 是专为公共语言基础结构(CLI)设计的。CLI 由可执行代码和运行时环境组成,允许在不同的计算机平台和体系结构上使用各种高级语言。
b.发展
.NET Framework ⇒.NET (Core)
-----------------------------------------------------------------------------------------------------
NET Framework(1.0 —— 4.8.1)
.NET Framework是基于Windows系统的.NET框架,从2002年发布,到最新的4.8.*版本,已经停止发展。
最后的4.8.*版本依然还在维护,还是可以使用的,支持的最低操作系统是Windows 7。
如果要运行在XP系统上,则只能使用.NET Framework4版本,支持最低Windows XP SP3。
.NET Framework是基于Windows系统的,因此也只能在Windows系统上运行。
-----------------------------------------------------------------------------------------------------
NET Core(Core1/2/3,5/6/7/8/9)
从2016年发布首个.NET Core1,和后面的.NET Core2/3、.NET 5/6/7/*是一个体系的,
只是从.NET5开始更改了命名。这是微软推出的新一代.NET框架,用来代替原有的.NET Framework,
核心特点就是开源、跨平台,这也是.NET未来重点发展、投资的地方。
c.C#成为一种广泛应用的专业语言的原因
现代的、通用的编程语言。
面向对象。
面向组件。
容易学习。
结构化语言。
它产生高效率的程序。
它可以在多种计算机平台上编译。
.Net 框架的一部分。
d.C#一些重要的功能
布尔条件(Boolean Conditions)
自动垃圾回收(Automatic Garbage Collection)
标准库(Standard Library)
组件版本(Assembly Versioning)
属性(Properties)和事件(Events)
委托(Delegates)和事件管理(Events Management)
易于使用的泛型(Generics)
索引器(Indexers)
条件编译(Conditional Compilation)
简单的多线程(Multithreading)
LINQ 和 Lambda 表达式
集成 Windows
02.NET Framework框架
a.概念
a.介绍
.NET Framework 是运行在 Windows 系列操作系统上的一个系统应用程序。
它是 .NET 的核心部分,提供了建立和运行 .NET 应用程序所需要的编辑、编译等核心服务。
它包括2个重要组成部分:
公共语言运行时 (Common Language Runtime,CLR)
.NET Framework 类库(Framework Class Library,FCL)
b.对于Framework
.NET Framework设计之初就是基于Windows系统的,其API与Windows底层接口紧密关联,
所以DotNET应用也只能运行在Windows系统上了。
同时,.NET Framework也作为Windows操作系统的组件内置在操作系统中。
同一台电脑上可以同时安装多个版本的 .NET Framework 版本。
c.总结
.NET Framework = 运行时CLR + 类库FCL。
b.运行时(CLR),核心
a.概念
公共语言运行时 (Common Language Runtime,CLR),
CLR 本质上就是.NET 虚拟机(类似Java的虚拟机JVM),
算是.NET的引擎,用来执行托管.NET代码,确切的说是编译后的IL代码。
提供管理内存、线程执行、代码执行、代码安全验证、异常处理、编译、垃圾回收等运行时服务。
b.组成
类加载器(Class Loader):加载程序集的类(包括方法)、元数据,及其他程序所需组件。
即时编译器JIT(Just-In-Time,简称JIT ):负责将中间代码MSIL翻译成本机执行代码(MSIL to Native Code)。
代码管理器(Code Manager):管理代码的执行。
垃圾回收器GC(Garbage Collector):负责整个.NET运行时托管代码的内存分配与释放任务,它通过一定的优化算法选择收集对象和时间,并进行自动的垃圾收集。
安全引擎(Security Engine):提供基于认证的安全机制,如用户身份。
调试引擎(Debug Engine):使开发者能调试和跟踪应用程序代码。
类型检查器(Type Checker):检查并禁止非安全的类型转换以及未初始化的变量的使用。
异常管理器(Exception Manager):提供结构化的异常处理,与Windows结构化异常处理机制(SHE)集成,改进了错误报告。
线程支持(Thread Support):提供了多线程编程的类和接口。
c.CTS(通用类型系统)与CLS(通用语言规范)
a.概念
.NET CLR是支持多种语言的,如C#、F#、VB等,这些语言编译后都可以在CLR上执行,
还支持不同语言的互操作,比如在C#类继承一个VB写的类,在C#中调用F#写的一个方法等。
-------------------------------------------------------------------------------------------------
实现这种不同语言之间交互的基础就是这些语言都遵循统一的规范和要求 —— CTS(通用类型系统),
CTS定义了一套通用的数据类型系统,包括值类型、引用类型的结构。
比如C#中int、VB中的Interger都是整形,对应CTS中的Int32。
-------------------------------------------------------------------------------------------------
CLS是CTS的子集,规定了语言编译器必须遵循的一组规则,以便创建在 CLR 中运行的 .NET 应用程序。
可以把CTS看做是规范,CLR是对CTS的具体实现。
b.CTS(Common Type System)通用类型系统
微软制定了一个正式的规范来描述类型的定义和行为,这就是“通用类型系统”(Common Type System,CTS)。
所有.NET语言共享这一类型系统,实现它们之间无缝的互操作,比如可以在VB.NET中派生一个由C#编写的类。
CTS 提供了2种类型:引用类型、值类型。
CTS 规范规定,一个类型可以包含零个或多个成员。这些成员包括:字段(Field)、属性(Property)、方法(Method)、事件(Event)。
CTS 指定了类型可见性规则以及类型成员的访问规则,private、public 等。
CTS 还为类型继承、虚方法、对象生存期等定义了相应的规则。
CTS 规定:所有类型最终必须从预定义的 System.Object 类型继承,这里包括值类型也是从Object继承而来的。
c.CLS(Common Language Specifition)公共语言规范
公共语言规范(Common Language Specifition,CLS)是CTS的一个子集,
从类型、命名、事件、属性等方面对语言进行了共性的定义及规范。
CLS制定了一种以.NET平台为目标的语言所必须支持的最小特征,
以及该语言与其他.NET语言之间实现互操作性所需要的完备特征。
例如,CLS并不去关心一种语言用什么关键字实现继承,只是关心该语言如何支持继承。
d.DotNET Framework 类库(FCL/BCL)
类库(Framework Class Library,FCL)就是.NET Framework内置的各种组件服务,
如ASP.NET、MVC、WCF和WPF等组件,满足不同编程应用场景的需求。
-------------------------------------------------------------------------------------------------
基础类库BCL(Base Class Library)是FCL的一个子集,顾名思义就是一些比较基础、通用的类库,
如基本数据类型、集合、线程、安全、字符串操作、网络操作、IO、XML操作等等,大多都包含在System命名空间下,
如System.Text、System.IO。其他一些常用的名词,如核心 .NET 库、框架库、运行时库、共享框架,
大多都指的是BCL。
e.语言&运行机制
.NET是为了支持多种语言而设计的,如Microsoft自己发布的VB.NET、Managed C++、C#、J#和JScript.NET,
还有其他厂商开发的语言,如Cobol、Smalltalk、Perl和 Eiffel等几十种编程语言。
-----------------------------------------------------------------------------------------------------
无论使用哪种语言来编写源代码,都会通过编译器编译成中间代码MSIL(Microsoft Intermediate Language)。
语言的集成性主要是公共语言规范(CLS)和通用类型系统(CTS)一起确保了语言的相互操作性。
CLS是一个最低标准集,所有面向.NET的编译器都必须支持它,每个语言都符合这个规范,
从而满足了语言的互相调用和互操作。
-----------------------------------------------------------------------------------------------------
每种语言都有自己的编译器,将源代码编译成中间语言(MSIL,或IL),然后在CLR上运行,主要分为两个阶段:
编译阶段:由MSBuild把源代码编译为中间语言IL代码,编译后就是EXE、DLL格式的文件。
运行阶段:CLR把中间语言CIL代码编译为平台专用的机器代码,最终在CPU上运行的就是机器码(汇编代码)。
03.NET Core 框架
a.概念
.NET Core是一个免费、跨平台、开源的开发平台,用于一站式构建不同类型的应用程序。
.NET Core 是以.NET Framework 为基础,但是经过重新设计、实现的的新一代框架,
实现了原.NET Framework 中的几乎所有功能,核心特点就是开源、跨平台。
-----------------------------------------------------------------------------------------------------
开源,采用MIT和Apache协议作为开源协议,对商业十分友好。
跨平台,支持Windows、MacOS、Linux,支持x64,、x86、ARM架构。
友好通用,除了跨平台,.NET Core通过.NET Standard与.NET Framework、Xamarin和Mono兼容,支持WEB、移动、桌面、微服务容器、云端Azure、物联网IOT、机器学习、游戏Unity等多种应用开发模型。
支持多种语言,C#、VB、F#,基于一致的API标准模型——.NETStandard,使得不同语言的程序集和库可共享通用。
现代高效:结合C#8+、VS2019+,提供现代化的语言框架、高效的开发体验。
高性能,根据 TechEmpowers 发布的一份报告,.NET Core比其他同类框架都要快得多。
轻量,.NET Core可以打包在应用程序中,也可以安装在并行用户、机器范围内或服务器上,可以部署在Docker容器中。
-----------------------------------------------------------------------------------------------------
.NET Core 从2016年发布首个.NET Core1,后面陆续发布了.NET Core2、.NET Core3,及后续的.NET6、.NET7、.NET8、.NET9。
跳过了版本号4.x,以避免与 .NET Framework 4.x 混淆。
从.NET5开始,名称中删除了“ Core”,以强调这是.NET未来的主要实现。
ASP.NET Core 5.0 基于 .NET 5.0,但保留名称“ Core”以避免将其与 ASP.NET MVC 5 混淆。
同样,Entity Framework Core 5.0保留名称“ Core”以避免将其与 Entity Framework5、Entity Framework6 混淆。
b.统一的.NET框架
.NET框架体系经过多年发展,衍生了多个版本 —— 使用 .NET Framework 来构建 Windows 桌面应用程序,
使用 Xamarin 来构建 iOS 或 Android 应用程序,使用 .NET Core 来构建跨平台的 Web 应用程序。
每一个框架都有自己的BCL、运行时CLR,长此以往,这也是造成如今.NET市场越来越差的原因之一。
so,为解决这个问题,.NET Core就肩负了这个一统江湖的使命!
.NET 3 添加了对WinForm、WPF、EF的支持。
.NET 5统一了Mono和.NET Core BCL的代码库。
在.NET 6中,运行时和工具也统一了,完成了One .NET愿景。
.NET 7 统一了所有不同的 .NET 开发工具组件,使开发人员能够在相同的基础类库 (BCL)、运行时和编译器上构建所有类型的应用(桌面、移动、Web 等)。
从.NET5,到.NET7,统一的愿景基本已达成,一站式构建 Web、桌面和移动等应用,支持传统的WPF、Winfrom(当然这两者只限于Windows系统),还引入了.NET Multi-platform App UI (.NET MAUI)作为Xamarin Forms的继任者。
c.NET Core体系结构
运行时.NET Runtime 有两种,CoreCLR,功能和.NET Framework的CLR差不多;.NET Native RunTime(CoreRT/NativeAOT)是直接将C#代码编译为机器码的运行时,还只是一个实验性的运行时。
BCL/CoreFX,类库,即 Base Classlibrary 基础类,包含 File、System、Console、XML、ADO.NET、日期时间等类库。Core FX就是.NET Core的BCL。
App Model,一站式构建 Web、桌面和移动等应用的模型。
d.JIT、AOT编译
通常情况下,我们写的C#代码被编译为中间代码IL,在运行期间由CLR的JIT编译器编译为机器码执行。
这样势必造成运行的一些延迟,如果知道目标平台,则可以为该目标平台提前编译为机器码,
这就是所谓的提前编译(AOT),它的优点是启动时间更快。
JIT(Just In Time Compiler,即时编译),顾名思义,在需要(运行代码)的时候才会被编译为机器码。
当然编译好后会被缓存,以便于后续执行,这也是为什么.NET程序预热后执行更快的原因,不过程序关闭就没了。
AOT编译(Ahead of Time Compilation,预先编译或静态编译),在构建(发布)时将高级语言编译为本地机器码,
让程序更快的启动、运行。可以将.NET Core程序编译成原生(机器)代码,不依赖 .NET 运行时而运行在宿主机器上。
04.NET Framework vs NET Core
a.对比
| 比较 | .NET Core | .NET Framework
|---------------|----------------------------------------|------------------------------------
| 历史 | 2016年,.NET Core 是 .NET 的最新实现。 | 2002年,.NET Framework 是 .NET 的第一个实现
| 开源 | 完全开源 | 部分组件是开源的
| 维护团队 | .NET基金会(.NET Foundation) | 微软.NET团队
| 跨平台 | 支持跨平台,Windows,Linux和macOS | 仅Windows平台
| 第三方支持 | 有大量的第三方包,但少于.NET Framework | 历史悠久,有许多第三方软件包、库可用
| 应用内部署 | 支持In-app模式部署 | 需要单独安装SDK,不支持打包到应用内
| 移动开发 | 支持,Xamarin、MAUI | 不支持
| 性能和可扩展性 | 更高性能和可伸缩性 | 一般,性能和可伸缩性方面效率较低
| 微服务实施 | .NET Core 支持微服务的实现 | 不支持
| REST 服务实现 | 可以使用 .NET Core 创建一个 REST API。 | 它支持 REST API 服务实现
b.被遗弃的功能
.NET Framework 中有一些技术在 .NET 中已经废弃或调整了:
Windows相关API不支持:.NET Framework是深度集成Windows系统的,而.NET Core是跨平台的,因此就有大量的系统相关的API不支持。
Web 浏览器控件:桌面开发中的浏览器控件,之前是基于IE内核的,已经过时。新提供的 Microsoft.Web.WebView2 可以通过Nuget获得。
应用配置 App.config 升级为 appsettings.json,如果还想要用,则可添加System.Configuration.ConfigurationManager NuGet 包。
应用程序域 (AppDomain) ,不支持创建其他应用域,也尚未计划在将来添加此功能。 对于代码隔离,将流程或容器用作备用。 若要动态加载程序集,请使用 AssemblyLoadContext 类。
.NET Remoting,.NET 6 及更高版本不支持 .NET 远程处理。对于简单的跨进程通信,可将进程间通信 (IPC) 机制视为远程处理的备用方案。对于跨计算机的通信,可将基于网络的解决方案用作备用方案。
不支持 System.EnterpriseServices (COM+)。
代码访问安全性,WPF for .NET 删除与 CAS 相关的代码,公开定义的 CAS 相关类型被移出 WPF 程序集,并移入了 Core .NET 库程序集中。
Windows Workflow Foundation (WF) 和 Windows Communication Foundation (WCF),在.NET 5 及更高版本不支持 WF 和 WCF, 有关替代方法,请参阅 CoreWF 和 CoreWCF。
保存反射生成的程序集,.NET 6 及更高版本不支持保存由 System.Reflection.Emit API 生成的程序集,作为替代方法,请考虑 ILPack 库。
c.桌面开发能用.NET Core吗
开发服务端后台服务肯定是首选.NET Core的,那传统的的Windows桌面程序(WinForm、WPF)呢?
能不能用最新的.NET Core呢?—— 答案当然是可以的!
优点:
可以使用更新的C#语法、.NET特性,提升开发效率。
性能提升明显。
可以打包运行库的依赖,不用单独安装运行库了。
WebView2,基于Edge的现代浏览器内核。
缺点:
.NET Framework配套的第三方库比较丰富,可能没有.NETCore版本的。
对操作系统的兼容,.NETFramework4支持XP系统,Core最低支持Win7。
05.其他
a.介绍
.NET应用:就是使用了.NET框架开发的应用程序,基于CLR运行。
Assembly:程序集,源代码编译的产出,一般表现为.DLL或者.EXE格式,必须依靠CLR才能顺利执行。它们都是标准的PE格式的文件,主要包括PE(Portable Executable)头、CLR头(托管代码的一些信息,如类型元数据)、IL代码、其他资源数据。
DLL:Dynamic Link Library,动态链接库,包含了已编译的C#代码、元数据和资源,可以在多个应用程序之间共享和重用。
命令行 (CLI:Command-Line Interface) 是一种跨平台工具,用于开发、构建、执行发布 .NET 应用程序。是.NET Core SDK的一部分,可以用“dotnet”指令来检查是否正确安装。
MSBuild Microsoft Build Engine)是由Microsoft开发的构建工具和构建系统。它用于自动化软件项目的构建过程,包括编译源代码、打包、运行测试、部署和生成文档等。使用 MSBuild,我们可以构建Visual Studio项目和解决方案,而无需安装Visual Studio IDE。
b.托管代码和非托管代码
托管代码(Managed Code)是指在受管理环境中运行的代码,通常指的是.NET语言(C#、VB)开发的代码,
在CLR运行时环境中运行。由CLR来负责内存管理、垃圾回收、类型安全性和异常处理等任务,
所以我们写(C#)托管代码时,就可以随意申明变量、创建对象,而不用去管内存的分配、管理和回收,
使得代码更容易编写、维护和调试。
-----------------------------------------------------------------------------------------------------
Java也算一种托管代码,相对而言,非托管代码(Unmanaged Code)就是没有专门的执行管理环境,
通常使用低级编程语言(如C、C++)编写,开发人员需要手动处理内存管理、异常和资源释放等问题。
-----------------------------------------------------------------------------------------------------
在实际的C#的开发中,我们也会经常用到非托管资源,如IO、文件流操作,就需要注意要手动释放(非托管)资源,避免内存溢出。
c.NET Standard
.NET Standard 是针对多个.NET框架 实现推出的一套正式的 .NET API 规范,
目的是在多个.NET 框架间共享代码,如在.NET Framework 、 .NET、Mono间共享代码,提高 .NET 生态系统中的一致性。
.NET Standard 是一组标准化的 API规范,“.NET Standard Library”指的也是这个API标准库,注意只是一组规范,具体实现就是 .NET Framework、NET Core 和 Mono了,看看其源码就知道了。
-----------------------------------------------------------------------------------------------------
.NET Standard是针对跨平台框架的一种规范,在跨平台框架开发是才会涉及。
从 .NET 5 和 .NET 6 开始,微软就统一了所有平台、应用终端,不再需要.NET Standard了,它以后不会更新了。
对于.NET Framework,在4.5版本才正式支持.NET Standard的规范,.NET Core天生就支持了。
d.NET的ECMA标准
ECMA(European Computer Manufacturers Association,欧洲计算机制造商协会)是一个国际性的标准化组织,致力于制定和推广信息和通信技术的标准。
.NET体系中的C#语言、公共语言基础(CLI)也是有标准规范的,参考Ecma 标准。.NET主要的的规范标准包括:
e.Mono/Xamarin
Mono 是另一个 .NET Framework 的跨平台开源版本,并不属于微软,而是由社区的力量所主导。
Mono 项目始于 2001 年,由Xamarin牵头维护 mono/mono ,2016年Xamarin被微软收购,之后逐步成为.NETCore的一部分。
Mono 基于C#的ECMA标准和公共语言运行时(CLR),包括Mono运行时、C#编译器、类库和一些附加工具。Mono兼容.NET标准,并支持跨平台开发。可以在多个操作系统上运行,包括Linux、macOS和Windows。Mono比较流行的应用场景就是Unity游戏开发和移动端应用开发。
Unity游戏引擎使用Mono作为其脚本引擎的一部分,所以Unity游戏开发的主要语言就是C#。
Xamarin(杂么瑞):就是基于Mono,专门用来开发移动端App应用程序,实现一次开发多端应用。其继任者是微软在.NET7中推出的MAUI(maui /ˈmaʊɪ/ n.毛伊岛)。
99.从 C# 的语言特性看前世今生
a.C# 的诞生之路
其实,C# 的诞生可以追溯到20世纪90年代末。
当时,微软公司意识到互联网的迅速发展和软件复杂性的增加对开发者提出了新的挑战。
传统的编程语言无法满足日益增长的需求,开发者需要一种更强大、更现代化的语言来应对这些挑战,因此 C# 应运而生。
-----------------------------------------------------------------------------------------------------
C# 旨在提供更好的生产力、更强大的面向对象支持和更高的性能。
它从 C++、Java 和 Delphi 等语言中吸取了灵感,并结合了微软自身的创新,于2000年首次发布。
随着时间的推移,C# 逐渐发展成为 .NET 平台的主要编程语言之一,
为开发者提供了强大而灵活的工具来构建各种类型的应用程序。
b.C# 的设计目标和发展理念
在我看来,C# 的设计目标和理念是为了提供一种强大、现代化的编程语言,以提高开发者的生产力和代码质量。
C# 的首要设计目标是简单易用。它采用了类似于 C++ 和 Java 的语法,使得开发者能够迅速上手,
并编写清晰、简洁的代码。而且它还是一种纯面向对象的编程语言,支持封装、继承和多态等面向对象的特性,
因而十分有助于组织和管理代码,提高代码的可维护性和可扩展性。当然,安全性也是 C# 的关注重点,
它引入了类型安全检查、空引用检查和异常处理等机制,以防止常见的编程错误和安全漏洞,
提高代码的可靠性和稳定性。同时 C# 致力于提供高性能的代码执行。
-----------------------------------------------------------------------------------------------------
通过即时编译(Just-In-Time Compilation)技术,C# 代码可以被转换为机器码,并在运行时进行优化,从而提高程序的性能。
此外,C# 还追求跨平台开发,引入了 .NET Core 平台,最后演化成一个统一平台 .NET,使得 C# 可以在不同的操作系统上运行,
包括 Windows、Linux 和 macOS 等,扩展了应用程序的部署范围。并且集成开发环境(IDE)支持也是 C# 设计目标的一部分。
借助强大的IDE工具(如Microsoft Visual Studio/Visual Studio Code 和 Rider),我们可以进行代码编辑、调试、自动完成
和重构等操作,有效提高开发效率。
-----------------------------------------------------------------------------------------------------
综上所述,C# 基于简单易用、面向对象、安全性、高性能、跨平台和 IDE 支持等特点使得其成为广泛应用于各个领域的强大编程语言之一。
c.C# 的发展现状
值得注意的是,C# 目前处于极度被低估的状态,这主要是和微软公司过去的闭源政策有很大关系,
在2014年11月12日,微软宣布 .NET 平台全技术栈开源并且托管到 .NET 基金会,
将让 .NET 运行在 Linux 和 MacOS 上,并且会通过 .NET Foundation(.NET 基金会)来与社区密切合作在未来改进 .NET。
目前我们可以在 GitHub 上找到所有 .NET 平台组件的开源项目,包括 C# 的编译器。
-----------------------------------------------------------------------------------------------------
在带 GC 和性能尚可的系列主流工程语言里,C# 率先支持 AOT 创建动态库,这样一来,C++的任何场景,
所有桌面端开发、WebAssembly、大部分移动端(包括安卓、iOS),C# AOT 都能参与 (不是完全替代,而是参与),
而语言各有所长,只是参与就能带来很多改变。
-----------------------------------------------------------------------------------------------------
最新版本的 C# 已经是一个现代化的编程语言,C# 在2022年和2023年发布了改变游戏规则的特性是上下游语言无关的代码复用。
无论是 Java、Python、JS 还是 Rust 开发,我们都可以使用 C# 代码库,轻量、无需引入其他运行时和构建依赖,
这充分发挥了 C# 代码优雅的优势,让上层应用的程序员也能做底层的东西。
d.C# 除了是现代的编程语言,还有以下亮点:
跨平台:.NET 8支持跨当前所有主流的桌面、移动平台。C# 12可以对标 gcc/g++。
高层级 GC 支持,屏蔽底层细节:.NET 有可配置的 GC,.NET GC 的实现虽然不如很多 JVM 的 GC 实现,但也是身经百战,经过了高并发的考验。
跨语言复用性,全面超越同时代的编程语言:相比榜单上靠前的 Java、Python、JavaScript 这些语言,C# 支持 AOT 输出单个动态链接库,能定义导出C函数符号,那么交付方不管使用什么语言,我们都可以用C#进行操作。并且我们还可以切换到 bflat 工具链,不用安装 .NET SDK,实现快速编译。
支持值类型,让开发者完全掌控与 C 的互操作:有了动态库的优势,加上具有原生的值类型,在 C# 里定义 C API 接口也是极简单的。值类型能实现运行时无关的内存布局,在导出动态库时,导出函数的传参规范时可直接使用 C 调用约定,在这过程中我们需要注意返回值大小不要超过 intptr_t 即可。
导出函数声明简单:使用 UnmanagedCallersOnly 特性可以将 C# 函数导出提供给 C 调用,如果你去领略 C/C++ 的 DLL_EXPORT 四亿种写法,各个项目各有千秋符合人体工学的命名空间设计,对开发者的要求更低。这点和 Java 类似,远比 C++ 好。
兼容性好:标准库 API 比较稳定,方便代码移植。
功能全面的标准库:.NET 标准库很全,质量非常高,为 C# 在各个应用场景下的应用铺平了道路。社区里经常听到很多人在说 C# 库少,只需登录 GitHub 便可验证其真假,没有几个编程语言能和 C# 拼库多,一个用了这么多年一直排名前几的语言库不可能少。尤其是再考虑库的覆盖广度上就更少有能相比的了,和上面同样的原因,有很多编程语言看似库多,但都是重复的。
性能上限足够高:性能上限很多评测已经给出了,最新的 .NET 8 在少数测试中甚至可以略微超过 C++。就算综合来看,C/C++,Rust 是第一梯队的话,C# 也是之下第二梯队了,Go 和 Java 只能算是第三梯队级,虽然很多人把 Java 和 C# 经常一起对比,但是目前在性能上限来说,C# 和 Java 已经有了断档级别的差距。
使用下限足够低:基本上只要学过编程就能很快上手,可以完成基本功能。在易用性来说,Golang 确实是第一梯队,C# 和 Java 算是第二梯队,而 C++ 和 Rust 的系统对于新手而言则难于上青天。
99.从 C# 的应用场景看未来发展
a.人工智能
毋庸置疑,人工智能场景应用是 C# 最有发展潜力的方向,回顾近10年的算法发展,主要经历了3个阶段:
利用数学及统计知识,设计特征,进行处理和判别的传统算法阶段,采用深度神经网络,标注数据进行训练,
自动提取特征,进行处理和判别的深度学习算法阶段,基于大模型,零样本(Zero-Shot)处理,
或采用少量样本微调的大模型应用开发阶段。前两个阶段 C# 有 ML.NET 库可以做这方面的应用开发,
大模型的 Zero-Shot 特点和泛用性,使得完成一件事情,不再需要经历收集数据、标注、训练、优化这个完整的过程,
只需要理解,即可进行应用层开发。
-----------------------------------------------------------------------------------------------------
C# 语言的某些特性,包括对非结构化数据友好,支持 Native AOT,跨平台,Blazor 等特别适合人工智能类应用,
使得它十分适合开发人工智能产品。例如,在应用层的开发上,微软2023年3月份开源的 Semantic Kernel,
在2023年12月 20日正式发布了1.0 版本。Semantic Kernel 的出现,直接打开了 C# 通向 AGI 的大门。
b.云原生
云原生、容器技术发展带来整体后端工业化、生态的进步,我们也可以基于 k8s 本身服务构建分布式架构,
共享所有语言的中间件。比如:你可以基于 Dapr 使用任何语言的任何中间件,C# 正好是 Dapr 支持的第一语言。
.NET 8 重塑了我们构建可按需扩展的智能、云原生应用程序和高流量服务的方式。
无论我们是部署到 Linux 还是 Windows,使用容器还是我们选择的云应用程序模型,
-----------------------------------------------------------------------------------------------------
.NET 8 都可以让我们更轻松地构建这些应用程序。
它包括一组经过验证的库,目前 Microsoft 的许多大规模服务都在使用这些库,
可以帮助我们应对可观察性、弹性、可扩展性、可管理性等方面的基本挑战。
-----------------------------------------------------------------------------------------------------
.NET 8 支持将 .NET 应用程序编译为本机代码,使用更少的内存并立即启动,无需等待 JIT(即时)编译器在运行时编译代码,
也无需部署 JIT 编译器和 IL 代码。AOT 应用程序只部署应用程序所需的代码。
我们的应用程序现在可以在无法使用 JIT 编译器的受限环境中运行。
c.游戏
值得一提的是,C# 在游戏开发领域口碑也非常不错,
具体主要在学习上手、招聘人才、编程规范、运行性能方面都表现十分平衡,没有明显缺陷。
游戏开发人员使用 Unity 构建游戏,典型代表便是腾讯的国民游戏王者荣耀,它主要使用 Unity 打造,
已经拥有超过上亿的活跃用户。C# 与 Unity 引擎无缝集成,此外,还有一些新兴的游戏引擎也采用了 C# 语言,例如开源 godot 引擎。
除游戏前端,C# 和 .NET 技术在游戏后端(游戏服务器)也获得了越来越广泛的应用,C# 的服务器可以无障碍在 Linux 上运行。
d.全栈 Web
.NET 8 中的 Blazor 可以同时使用服务器和客户端来处理我们的所有 Web UI 需求。
它通过专注于优化页面加载时间、可扩展性和提升用户体验的多项新增强功能,
开发人员现在可以在同一应用程序中使用 Blazor Server 和 Blazor WebAssembly,
在运行时自动将用户从服务器转移到客户端。两者对比之下,显然 .NET 代码在 WebAssembly 上的运行速度更快,
这主要归功于新的基于“Jiterpreter”的运行时和新的内置组件。
总之,Blazor 填补了欠缺的一环,C# 开发人员可以直接在用户浏览器中共享代码和业务逻辑,
对于 C# 开发人员来说,这无疑是一项十分强大的功能,有效提升我们的工作效率。
e.桌面应用
其实,C# 在桌面开发框架上的应用也是一大亮点。在 .NET 平台上传统的 Windows 桌面开发框架WPF 和 Winforms 之外,
还有很多跨平台的开源框架,比如 Avalonia UI、UNO Platform。 特别Avalonia UI 是一个基于 .NET 和 XAML 的偏桌面端
的跨平台(WINDOWS、LINUX、MAC)UI 开发框架,不存在某个平台用 WINUI,某个平台用原生,导致不同平台渲染会出现差别的
问题发生,所有平台都使用 SkiaSharp 来渲染。
特别是在国内,在 openKylin 社区成立了 Avalonia SIG,推进 Avalonia UI 对 openKylin 生态的支持,
及 Avalonia UI 的相关生态建设。随着国内的 Linux 桌面向工业、医疗等传统行业渗透,Avalonia UI 在这个市场上
有着充分的竞争力,有越来越多的用户从传统的 QT 转移到开源免费的 Avalonia UI。
除了 Avalonia UI 之外,还有 Blazor 在客户端应用上表现同样出色。从 .NET7 版本开始支持的 Blazor Hybird,
使用 Blazor Hybrid 将桌面和移动本机客户端框架与 .NET 和 Blazor 结合使用。在 Blazor Hybrid 应用中,
Razor 组件在设备上本机运行。组件通过本地互操作通道呈现到嵌入式 Web View 控件。组件不在浏览器中运行,
并且不涉及 WebAssembly。Razor 组件可快速加载和执行代码,组件可通过 .NET 平台完全访问设备的本机功能。
f.移动应用
.NET 多平台应用程序 UI (.NET MAUI) 是一个跨平台框架,用于使用 C# 和 XAML 创建本机移动和桌面应用程序,
使用 .NET MAUI,可以开发可在 Android、iOS、macOS 上运行的应用,Windows 以及从单个共享代码库运行的应用。
.NET MAUI 包含 BlazorWebView 控件,该控件运行将 Razor 组件呈现到嵌入式 Web View 中。
通过结合使用 .NET MAUI 和 Blazor,我们可以跨移动设备、桌面设备和 Web 重复使用一组 Web UI 组件。
g.物联网 IoT
除桌面和移动应用外,C# 在物联网 IoT 的应用在当前也较为普遍。.NET 可在各种平台和体系结构上运行。
它支持 Raspberry Pi 和 Hummingboard 等常见物联网 (IoT) 插件板。
IoT 应用通常与专用的硬件(例如传感器、模数转换器和 LCD 设备)交互。
在网络化、标准化或网络安全方面,对工业网络的要求正以非凡的速度增长。
在这些问题重重的领域,基于以太网的 OPC UA(Open Platform Communications – Unified Architecture,
开放平台通信 - 统一架构)通信标准正在快速发展,OPC 基金会出品的 OPC UA 的标准库 UA-.NETStandard
就是使用 C# 实现,允许非会员使用 OPC UA 的试用规范、原型或研究项目。
其实,物联网的后端系统使用 .NET 和 C# 也获得了越来越广泛的应用,构建的服务后端系统可以无障碍运行在 Linux 上。
2 容器
2.1 数组
2.2 枚举
2.3 字符串
2.4 结构体
2.5 可空类型
2.6 集合
2.7 泛型
2.8 异常处理
3 对象
3.1 方法
匿名方法
3.2 类
01.类的命名规则:
标识符必须以字母、下划线或 @ 开头,后面可以跟一系列的字母、数字( 0 - 9 )、下划线( _ )、@。
标识符中的第一个字符不能是数字。
标识符必须不包含任何嵌入的空格或符号,比如 ? - +! # % ^ & * ( ) [ ] { } . ; : " ' / \。
标识符不能是 C# 关键字。除非它们有一个 @ 前缀。 例如,@if 是有效的标识符,但 if 不是,因为 if 是关键字。
标识符必须区分大小写。大写字母和小写字母被认为是不同的字母。
不能与C#的类库名称相同。
3.3 继承
3.4 接口
3.5 多态
3.6 命名空间
3.7 顶级语句
01.介绍
a.概念
在 C# 9.0 版本中,引入了顶级语句(Top-Level Statements)的概念,
这是一种新的编程范式,允许开发者在文件的顶层直接编写语句,而不需要将它们封装在方法或类中。
b.特点
无需类或方法:顶级语句允许你直接在文件的顶层编写代码,无需定义类或方法。
文件作为入口点:包含顶级语句的文件被视为程序的入口点,类似于 C# 之前的 Main 方法。
自动 Main 方法:编译器会自动生成一个 Main 方法,并将顶级语句作为 Main 方法的主体。
支持局部函数:尽管不需要定义类,但顶级语句的文件中仍然可以定义局部函数。
更好的可读性:对于简单的脚本或工具,顶级语句提供了更好的可读性和简洁性。
适用于小型项目:顶级语句非常适合小型项目或脚本,可以快速编写和运行代码。
与现有代码兼容:顶级语句可以与现有的 C# 代码库一起使用,不会影响现有代码。
c.注意事项
文件限制:顶级语句只能在一个源文件中使用。如果在一个项目中有多个使用顶级语句的文件,会导致编译错误。
程序入口:如果使用顶级语句,则该文件会隐式地包含 Main 方法,并且该文件将成为程序的入口点。
作用域限制:顶级语句中的代码共享一个全局作用域,这意味着可以在顶级语句中定义的变量和方法可以在整个文件中访问。
02.对比
a.传统 C# 代码
using System;
namespace MyApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}
}
b.使用顶级语句的 C# 代码
using System;
using System.Linq;
// 顶级语句中的变量声明
int number = 42;
string message = "The answer to life, the universe, and everything is";
// 输出变量
Console.WriteLine($"{message} {number}.");
// 定义和调用方法
int Add(int a, int b) => a + b;
Console.WriteLine($"Sum of 1 and 2 is {Add(1, 2)}.");
// 使用 LINQ
var numbers = new[] { 1, 2, 3, 4, 5 };
var evens = numbers.Where(n => n % 2 == 0).ToArray();
Console.WriteLine("Even numbers: " + string.Join(", ", evens));
// 异常处理
try
{
int zero = 0;
int result = number / zero;
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Error: " + ex.Message);
}
4 高级
4.1 事件
4.2 反射
4.3 多线程
4.4 网络框架
01.IOC框架
Unity、autofac、spring.net、MEF、Injection、Asp.Net Core的ServiceCollection