Skip to content

C# 起步:SDK、值、方法与对象 ​

安装 SDK 并首次运行 ​

C# 是语言,.NET 是运行平台和类库,SDK 包含编译与构建工具;只安装 Runtime 会出现 No .NET SDKs were found。本课程以 .NET SDK 10.0.402、net10.0、C# 14 为运行基线,核对于 2026-10-17。无需 Visual Studio 或 Windows 桌面组件,也能开发跨平台控制台应用。Xamarin、Windows Phone 是历史技术,不能从旧简介推断当前项目选型。

Windows 从 .NET 官方下载选择 x64 SDK 安装包,或把 SDK ZIP 解压到自己的目录。PowerShell 使用 & 'D:/tools/dotnet/dotnet.exe' --info 检查路径;Linux 选择对应架构的官方包或解压发行文件,通过 ./dotnet --info 检查。便携安装不必全局修改 PATH;下面的 dotnet 可替换为完整路径。Linux SDK 依赖 ICU 等系统库,缺库按发行版安装说明处理,不把关闭全球化视为通用修复。

shell
dotnet --list-sdks
dotnet new console -o Hello
cd Hello
dotnet run

默认程序输出 Hello, World!。Hello.csproj 定义目标框架,Program.cs 是源文件,obj 是构建中间产物,bin 是输出。dotnet build 编译,dotnet run 构建并启动,dotnet test 需要真正的测试工程,不能把没有测试的成功输出当测试通过。下面替换整个 Program.cs,可继续 dotnet run。

值、空值、运算与控制流 ​

csharp
int completed = 2;
const int goal = 3;
string? alias = null;
var scores = new[] { 80, 90, 100 };
int total = 0;
foreach (int score in scores) total = checked(total + score);
if (completed < goal) Console.WriteLine($"{alias ?? "reader"}: {total}");

输出 reader: 270。声明写作“类型 名称”;var 是编译期推断,不是动态类型。const 是编译期常量;局部变量未初始化不能直接读取。string? 明确可能为空,启用 Nullable 会让编译器跟踪空状态,但警告不是外部输入的运行期保证;! 只压制警告,不产生检查。优先 is null、?? 或抛出明确异常。整数除法 5 / 2 得2,5 / 2.0 得2.5;金额应选择整数最小单位或 decimal,而不是无意使用二进制浮点。

值类型赋值复制值;引用类型赋值复制引用。int[] copy = scores; copy[0] = 0; 会修改同一个数组;若需独立元素槽位可用 scores.ToArray(),元素若还是引用对象则仍是浅复制。checked 使整数溢出抛异常,业务范围还需单独校验。

方法、委托和异常 ​

参数表达依赖,返回值表达结果。下面也是完整 Program.cs:

csharp
static int Sum(IEnumerable<int> values) {
    int result = 0;
    foreach (int value in values) result = checked(result + value);
    return result;
}
Func<int, bool> accepted = n => n >= 90;
Console.WriteLine(Sum(new[] { 80, 90, 100 }.Where(accepted)));

输出190。IEnumerable<int> 是可枚举整数的接口;实现可以是数组、列表或延迟序列。Func<int,bool> 是接收整数、返回布尔的委托类型;lambda 捕获外层变量时捕获的是变量,不是创建时的值快照,异步回调中尤其需想清变量生存期。普通参数按值传递,引用类型仍是复制引用;ref 才允许修改调用方变量槽位,不应为省一次复制随意添加。

throw 结束正常路径;能恢复才 catch,重新抛原异常用 throw; 保留原堆栈。在 CLI 边界将错误映射到 stderr/退出码,在库中保留具体异常或结构化错误,不把所有失败变成0。

类、record、接口与模块 ​

类把状态和行为组合;接口表达调用契约,record 适合值导向数据。以下完整程序展示同一接口的一个实现,顶层语句必须位于类型声明之前:

csharp
IFormatter formatter = new PlainFormatter();
var first = new Study("types", 30);
Console.WriteLine(formatter.Format(first));
Console.WriteLine(first == new Study("types", 30));
public sealed record Study(string Topic, int Minutes);
public interface IFormatter { string Format(Study value); }
public sealed class PlainFormatter : IFormatter {
    public string Format(Study value) => $"{value.Topic}={value.Minutes}";
}

预期 types=30、True。record 生成值相等性和组件属性,但包含 List 时不会冻结 List;用不可变组件才能建立更强的不变量。类默认引用相等,不应依赖 == 在任意类上都比较字段。命名空间组织类型,项目引用表达编译依赖;using 导入名字不等于安装包。标准库项目不需要额外 NuGet 依赖。

自检:将 record 改为有相同构造参数和属性的普通 class(保留 new Study(...) 可用,且不重写相等方法)后解释相等行为;将 alias! 改为 alias ?? "reader" 解释运行时区别。参考答案:普通类未重写相等时按对象身份;抑制警告不会阻止空引用,默认值会真正选择替代字符串。继续学习 LINQ 与泛型。

依据:C# 类型系统、中文 C# 教程、Windows 安装。本文为独立讲解;实际运行平台和限制见工作包验证记录。

继续阅读