如何设置string类型为非可空(不可为null)?
你需要实现「未标记nullable的属性必须持有有效值,标记nullable的属性允许为null」的规则,完全可以依托C#本身的能力和框架机制实现,不需要在业务代码里零散写string.IsNullOrEmpty()判断,下面是可直接落地的方案,按落地成本从低到高排序:
1. 开启可空引用类型编译检查(零成本基础方案)
C# 8.0及以上版本原生支持可空引用类型(NRT),只需要在项目的.csproj文件中添加如下配置,就能打开编译期的空值检查:
<PropertyGroup> <Nullable>enable</Nullable> </PropertyGroup>
开启后编译器默认将所有未加?标记的引用类型识别为不可空类型,尝试给这类属性赋值null、或者未初始化就使用时,编译器会直接抛出警告,在编码阶段就能拦掉大部分空值问题。
对应你给出的实体属性,代码可以直接写成这样:
public class Book { // 只读属性 public int Id { get; } // 未加?标记,编译器默认要求非空 public string Title { get; set; } public string Author { get; set; } public int PageCount { get; set; } public int TimesBorrowed { get; set; } // 加?标记为可空,允许赋值null public string? Borrower { get; set; } public DateTime? BorrowDate { get; set; } public DateTime? DueDate { get; set; } // 构造函数初始化必填非空属性,编译器会自动检查是否所有非空属性都被赋值 public Book(int id, string title, string author, int pageCount) { Id = id; Title = title; Author = author; PageCount = pageCount; TimesBorrowed = 0; } }
注意:这个方案是编译期检查,如果涉及反射、反序列化(比如数据库查询结果转实体、接口参数反序列化)等场景,运行时可能绕过编译器检查给属性注入null,需要搭配运行时校验兜底。
2. 实体内部收敛校验逻辑(无额外依赖的运行时兜底)
把空校验逻辑直接写在属性的setter和构造函数里,不需要在业务代码中重复写判断,所有赋值操作都会自动走校验:
private string _title; public string Title { get => _title; set => _title = string.IsNullOrEmpty(value) ? throw new ArgumentException("书名不能为空", nameof(value)) : value; } private string _author; public string Author { get => _author; set => _author = string.IsNullOrEmpty(value) ? throw new ArgumentException("作者不能为空", nameof(value)) : value; }
只要有代码尝试给Title、Author赋值null或者空字符串,会在赋值环节直接抛出异常,不会让脏数据流入业务逻辑。如果用C# 11及以上版本,可以搭配required关键字修饰必填属性,进一步强制要求初始化时必须给这些属性赋值。
3. 数据注解校验(适配框架的最简方案)
如果你做的是Web项目、或者用EF Core做数据访问,直接用内置的数据注解标记必填属性即可,框架会自动完成校验,不需要自己写判断逻辑:
using System.ComponentModel.DataAnnotations; public class Book { public int Id { get; set; } [Required(ErrorMessage = "书名不能为空")] public string Title { get; set; } = string.Empty; [Required(ErrorMessage = "作者不能为空")] public string Author { get; set; } = string.Empty; public int PageCount { get; set; } public int TimesBorrowed { get; set; } public string? Borrower { get; set; } public DateTime? BorrowDate { get; set; } public DateTime? DueDate { get; set; } }
接口请求进来时,ASP.NET Core会自动校验带[Required]标记的属性是否为空,校验不通过会直接返回参数错误;EF Core生成数据库迁移时,也会自动将这些字段配置为非空列,从数据库层面再做一层空值拦截。
4. 自定义非空字符串值类型(强约束方案)
如果想从类型根源上彻底杜绝空值,可以封装一个专门的非空字符串值类型,所有要求非空的字段都用这个类型替代原生string:
public readonly struct NonEmptyString { public string Value { get; } public NonEmptyString(string value) { if (string.IsNullOrEmpty(value)) throw new ArgumentException("字符串不能为空", nameof(value)); Value = value; } // 配置隐式类型转换,减少使用时的类型转换代码 public static implicit operator string(NonEmptyString s) => s.Value; public static implicit operator NonEmptyString(string s) => new(s); }
使用时直接将Title、Author的类型声明为NonEmptyString即可,任何尝试给这类属性传null或者空串的操作,都会在实例创建时直接抛出错误,完全不存在空值漏判的可能。
选型建议:大学课程项目不需要过度设计,用「开启可空引用类型 + 数据注解/setter校验」的组合就足够,既可以在编码阶段靠编译器提示减少空值bug,运行时也有兜底校验,完全不需要在业务逻辑里零散写空判断。
内容的提问来源于stack exchange,提问作者Het Patel

