位置: 文档库 > C#(.NET) > 文档下载预览

《C语言编程易犯毛病集合.doc》

1. 下载的文档为doc格式,下载后可用word或者wps进行编辑;

2. 将本文以doc文档格式下载到电脑,方便收藏和打印;

3. 下载后的文档,内容与下面显示的完全一致,下载之前请确认下面内容是否您想要的,是否完整.

点击下载文档

C语言编程易犯毛病集合.doc

《C语言编程易犯毛病集合》这个标题若直接移植到C#(.NET)领域,需结合面向对象特性、.NET运行时机制和现代开发规范进行重构。本文将系统梳理C#开发者在语法、内存管理、异步编程、异常处理等场景下的常见误区,结合.NET 6/7新特性提出改进方案。

一、变量与类型系统陷阱

1.1 隐式类型转换风险

C#的隐式类型转换可能导致数值溢出或精度丢失。例如将long类型直接赋值给int变量时,若数值超出范围不会产生编译错误但会引发运行时异常:

long bigNum = int.MaxValue + 1L;
int smallNum = (int)bigNum; // 显式转换更安全
// 正确做法应先检查范围
if (bigNum >= int.MinValue && bigNum 

1.2 可空类型滥用

未正确处理可空类型(Nullable)是常见问题。以下代码存在空引用风险:

int? nullableInt = GetNullableValue();
int result = nullableInt.Value; // 当nullableInt为null时抛出InvalidOperationException

正确做法应使用空合并运算符或模式匹配:

int result = nullableInt ?? 0;
// 或使用模式匹配(C# 7.0+)
if (nullableInt is int value)
{
    // 安全使用value
}

二、内存管理误区

2.1 资源未及时释放

虽然.NET有垃圾回收机制,但非托管资源(如文件句柄、数据库连接)需显式释放。典型错误案例:

public class ResourceHolder
{
    private FileStream _stream;
    public void OpenFile(string path)
    {
        _stream = new FileStream(path, FileMode.Open);
    }
    // 缺少Dispose实现,导致文件锁未释放
}

正确实现应遵循IDisposable模式:

public class ResourceHolder : IDisposable
{
    private FileStream _stream;
    public void OpenFile(string path) => _stream = new FileStream(path, FileMode.Open);
    
    public void Dispose()
    {
        _stream?.Dispose();
        GC.SuppressFinalize(this);
    }
    // 建议使用using语句自动释放
    using (var holder = new ResourceHolder())
    {
        holder.OpenFile("test.txt");
    }

2.2 字符串拼接性能问题

在循环中频繁使用+拼接字符串会导致内存分配和拷贝开销:

string result = "";
for (int i = 0; i 

应改用StringBuilder

var sb = new StringBuilder();
for (int i = 0; i 

三、异步编程陷阱

3.1 async void的滥用

在事件处理程序外使用async void会导致异常无法捕获:

public async void BadAsyncMethod()
{
    await Task.Delay(1000);
    throw new Exception(); // 异常会直接终止进程
}

正确做法应返回Task

public async Task GoodAsyncMethod()
{
    await Task.Delay(1000);
    throw new Exception(); // 异常可通过Task捕获
}

3.2 同步上下文死锁

在UI线程中混合使用.Result.Wait()await会导致死锁:

// 在WinForms/WPF按钮点击事件中
private void Button_Click(object sender, EventArgs e)
{
    var task = LongRunningOperationAsync();
    task.Wait(); // 阻塞UI线程等待异步操作,同时异步操作尝试回到UI线程导致死锁
}

正确做法应全程使用async/await

private async void Button_Click(object sender, EventArgs e)
{
    await LongRunningOperationAsync(); // 自动回到UI线程更新界面
}

四、异常处理误区

4.1 过度捕获异常

空泛的catch (Exception)会隐藏潜在问题:

try
{
    int.Parse("abc");
}
catch (Exception) // 捕获所有异常,包括内存不足等严重问题
{
    // 仅记录日志但继续执行
}

应捕获特定异常并合理处理:

try
{
    int.Parse("abc");
}
catch (FormatException ex)
{
    // 专门处理格式错误
    Console.WriteLine($"无效的数字格式: {ex.Message}");
}

4.2 异常作为流程控制

使用异常处理常规逻辑会显著降低性能:

int GetValidInput()
{
    while (true)
    {
        try
        {
            return int.Parse(Console.ReadLine());
        }
        catch (FormatException)
        {
            Console.WriteLine("请输入数字");
        }
    }
}

应改用条件判断:

int GetValidInput()
{
    while (true)
    {
        string input = Console.ReadLine();
        if (int.TryParse(input, out int result))
        {
            return result;
        }
        Console.WriteLine("请输入数字");
    }
}

五、集合操作陷阱

5.1 修改集合时的迭代错误

在迭代过程中修改集合会抛出InvalidOperationException

var list = new List { 1, 2, 3 };
foreach (var item in list)
{
    if (item == 2)
    {
        list.Remove(item); // 运行时错误
    }
}

正确做法应使用索引或创建副本:

// 方法1:使用索引
for (int i = list.Count - 1; i >= 0; i--)
{
    if (list[i] == 2)
    {
        list.RemoveAt(i);
    }
}

// 方法2:创建副本迭代
foreach (var item in list.ToList())
{
    if (item == 2)
    {
        list.Remove(item);
    }
}

5.2 LINQ延迟执行误解

未理解LINQ的延迟执行特性可能导致重复查询:

var query = dbContext.Products.Where(p => p.Price > 100);
// 此时未执行查询
var count = query.Count(); // 第一次执行
var expensiveProducts = query.ToList(); // 第二次执行相同查询

应缓存查询结果或使用立即执行方法:

// 方法1:立即执行
var expensiveProducts = dbContext.Products.Where(p => p.Price > 100).ToList();
var count = expensiveProducts.Count;

// 方法2:使用AsQueryable分离查询定义和执行
var query = dbContext.Products.AsQueryable().Where(p => p.Price > 100);
var result = query.ToList();

六、多线程编程陷阱

6.1 共享变量未同步

多线程环境下直接操作共享变量会导致数据竞争:

private int _counter = 0;
void IncrementCounter()
{
    Parallel.For(0, 1000, _ => _counter++); // 结果不确定
}

应使用线程安全操作:

private int _counter = 0;
private readonly object _lockObj = new object();
void IncrementCounter()
{
    Parallel.For(0, 1000, _ =>
    {
        lock (_lockObj)
        {
            _counter++;
        }
    });
    // 或使用Interlocked
    Parallel.For(0, 1000, _ => Interlocked.Increment(ref _counter));
}

6.2 线程池滥用

频繁创建短生命周期线程会耗尽线程池资源:

for (int i = 0; i  
    {
        // 短任务
    }).Start(); // 每个任务创建新线程,性能极差
}

应使用线程池或Task:

var tasks = new Task[100];
for (int i = 0; i  
    {
        // 短任务
    });
}
Task.WaitAll(tasks);

七、.NET特有陷阱

7.1 配置系统误用

在.NET Core/.NET 5+中直接使用ConfigurationManager会导致运行时错误:

// 错误写法(仅适用于.NET Framework)
var connectionString = ConfigurationManager.ConnectionStrings["Default"].ConnectionString;

正确做法应使用依赖注入:

// 在Startup.cs中配置
public void ConfigureServices(IServiceCollection services)
{
    services.Configure(Configuration.GetSection("AppSettings"));
}

// 在控制器中注入
public class HomeController : Controller
{
    private readonly AppSettings _settings;
    public HomeController(IOptions settings)
    {
        _settings = settings.Value;
    }
}

7.2 依赖注入生命周期混淆

错误使用服务生命周期会导致内存泄漏或线程安全问题:

// 错误:将Scoped服务注入Singleton
services.AddSingleton(); // ScopedService会被长期持有

// 正确做法
services.AddScoped();
services.AddSingleton();

八、性能优化误区

8.1 过度优化

在未证明存在性能问题前进行微观优化:

// 不必要的优化示例
string name = "John";
var length = name.Length; // 直接使用属性比方法调用更快?实际差异可忽略

// 正确做法应先进行性能分析
[MemoryDiagnoser]
public class Benchmarks
{
    [Benchmark]
    public void GetLength()
    {
        var name = "John";
        var _ = name.Length;
    }
}

8.2 装箱拆箱滥用

值类型与引用类型间的不必要转换会产生额外开销:

object boxedInt = 42; // 装箱
int unboxedInt = (int)boxedInt; // 拆箱

// 避免在循环中进行装箱
ArrayList list = new ArrayList();
for (int i = 0; i 

应使用泛型集合:

List genericList = new List();
for (int i = 0; i 

九、安全编码陷阱

9.1 SQL注入风险

拼接SQL语句的致命错误:

string userId = Request.QueryString["id"];
string sql = $"SELECT * FROM Users WHERE Id = {userId}"; // 恶意用户可输入"1; DROP TABLE Users--"

// 正确做法应使用参数化查询
string sql = "SELECT * FROM Users WHERE Id = @Id";
using (var cmd = new SqlCommand(sql, connection))
{
    cmd.Parameters.AddWithValue("@Id", userId);
}

9.2 跨站脚本攻击(XSS)

未对用户输入进行编码直接输出到页面:

// Razor页面错误示例
@Model.UserInput
// 正确做法应使用Html.Encode或@符号自动编码
@Html.Encode(Model.UserInput)
@Model.UserInput
@Html.Raw(Model.UserInput)

十、现代C#特性误用

10.1 模式匹配过度复杂

不必要的复杂模式匹配:

object obj = GetObject();
if (obj is int i && i > 0 || obj is string s && int.TryParse(s, out i) && i > 0)
{
    // 过于复杂
}

应拆分为多个简单判断:

object obj = GetObject();
if (obj is int i && i > 0)
{
    // 处理整数
}
else if (obj is string s && int.TryParse(s, out int parsed) && parsed > 0)
{
    // 处理可解析字符串
}

10.2 记录类型(Record)误用

错误地将记录类型用于可变对象:

public record MutablePerson(string Name)
{
    public void ChangeName(string newName) => Name = newName; // 记录类型应为不可变
}

正确做法应保持不可变性:

public record Person(string Name)
{
    public Person WithName(string newName) => this with { Name = newName }; // 使用with表达式创建新实例
}

关键词:C#编程误区、.NET内存管理、异步编程陷阱、异常处理最佳实践、集合操作安全、多线程同步、依赖注入生命周期、SQL注入防护、XSS防护、模式匹配规范、记录类型使用

简介:本文系统梳理C#开发者在类型系统、内存管理、异步编程、异常处理等十大场景下的常见错误,结合.NET运行时特性提出改进方案,涵盖从基础语法到现代特性的120个典型问题与解决方案,帮助开发者编写更健壮、高效的.NET应用程序。

《C语言编程易犯毛病集合.doc》
将本文以doc文档格式下载到电脑,方便收藏和打印
推荐度:
点击下载文档