Skip to main content

csharp-nullable-types

Use when C# nullable reference types and value types for null safety, nullable annotations, and patterns for handling null values.

소스 정보

저장소
TheBushidoCollective/han
최근 소스 활동
2026년 2월 11일 17:47
감지된 SKILL.md 언어
영어
스타
198
포크
22

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
csharp-nullable-types
user-invocable
false
description
Use when C# nullable reference types and value types for null safety, nullable annotations, and patterns for handling null values.
allowed-tools
["Read","Write","Edit","Grep","Glob","Bash"]
# C# Nullable Types Nullable types in C# help prevent null reference exceptions, one of the most common programming errors. C# 8.0 introduced nullable reference types, providing compile-time null-safety checking. This skill covers nullable value types, nullable reference types, null-coalescing operators, and patterns for safe null handling. ## Nullable Value Types Value types (int, double, bool, struct) normally cannot be null. Nullable value types enable representing "no value" state. ```csharp using System; public class NullableValueTypes { // Declaration public void Declaration() { int? nullableInt = null; int? anotherInt = 42; double? nullableDouble = null; bool? nullableBool = true; // Generic syntax (equivalent) Nullable<int> genericNullable = null; } // Checking for null public void NullChecking() { int? value = GetNullableValue(); // HasValue property if (value.HasValue) { int actualValue = value.Value; Console.WriteLine(actualValue); } // Comparison with null if (value != null) { Console.WriteLine(value.Value); } // Null-conditional operator int? doubled = value?.GetHashCode(); } // Getting values public void GettingValues() { int? value = 42; // Value property (throws if null) int val1 = value.Value; // GetValueOrDefault int val2 = value.GetValueOrDefault(); // Returns 0 if null int val3 = value.GetValueOrDefault(10); // Returns 10 if null // Null-coalescing operator int val4 = value ?? 0; // Returns 0 if null } // Nullable in expressions public void NullableExpressions() { int? a = 10; int? b = 20; int? c = null; // Arithmetic (null if any operand is null) int? sum1 = a + b; // 30 int? sum2 = a + c; // null // Comparison bool? equal = a == b; // false bool? greater = a > c; // null // Logical operators bool? and = (a > 5) & (c > 5); // null } // Nullable structs public struct Point { public int X { get; set; } public int Y { get; set; } } public void NullableStructs() { Point? point = null; if (point.HasValue) { int x = point.Value.X; int y = point.Value.Y; } // Null-conditional with structs int? x = point?.X; } private int? GetNullableValue() => null; } ``` ## Nullable Reference Types C# 8.0+ provides nullable reference types, enabling compile-time null-safety for reference types. ```csharp #nullable enable using System; using System.Collections.Generic; public class NullableReferenceTypes { // Non-nullable by default public string Name { get; set; } = string.Empty; // Explicitly nullable public string? MiddleName { get; set; } // Constructor public NullableReferenceTypes(string name, string? middleName = null) { Name = name; // Must be non-null MiddleName = middleName; // Can be null } // Method with nullable parameters public string FormatName(string firstName, string? middleName, string lastName) { // Compiler warns if middleName used without null check if (middleName != null) { return $"{firstName} {middleName} {lastName}"; } return $"{firstName} {lastName}"; } // Nullable return type public string? FindPerson(int id) { // May return null return id > 0 ? "Found" : null; } // Collections of nullable types public void CollectionExamples() { // List of non-null strings List<string> names = new List<string> { "Alice", "Bob" }; // List of nullable strings List<string?> nullableNames = new List<string?> { "Alice", null, "Charlie" }; // Dictionary with nullable values Dictionary<string, string?> dict = new Dictionary<string, string?> { { "key1", "value1" }, { "key2", null } }; } // Nullable generic types public T? FindById<T>(int id) where T : class { // Returns null if not found return default(T); } // Not-null assertion operator public void NotNullAssertion() { string? maybeName = GetName(); // Tell compiler this won't be null (use with caution!) string name = maybeName!; } private string? GetName() => null; } ``` ## Null-Coalescing Operators Null-coalescing operators provide concise null handling. ```csharp #nullable enable using System; using System.Collections.Generic; public class NullCoalescingOperators { // Null-coalescing operator (??) public string GetDisplayName(string? name) { // Returns "Unknown" if name is null return name ?? "Unknown"; } // Chaining public string GetFirstNonNull(string? a, string? b, string? c) { return a ?? b ?? c ?? "Default"; } // Null-coalescing assignment (??=) public void NullCoalescingAssignment() { string? name = null; // Assign only if null name ??= "Default Name"; // name is now "Default Name" name ??= "Another Name"; // name stays "Default Name" } // With collections public void CollectionCoalescing() { List<string>? list = null; // Initialize if null list ??= new List<string>(); // Add items list.Add("item"); } // In property initialization private List<string>? _items; public List<string> Items => _items ??= new List<string>(); // Complex expressions public string ComplexCoalescing(User? user) { // Multiple levels return user?.Profile?.DisplayName ?? user?.Email ?? "Guest"; } public class User { public Profile? Profile { get; set; } public string? Email { get; set; } } public class Profile { public string? DisplayName { get; set; } } } ``` ## Null-Conditional Operator The null-conditional operator safely accesses members and calls methods on potentially null references. ```csharp #nullable enable using System; using System.Collections.Generic; public class NullConditionalOperator { public class Person { public string Name { get; set; } = string.Empty; public Address? Address { get; set; } public List<string>? PhoneNumbers { get; set; } } public class Address { public string City { get; set; } = string.Empty; public string? PostalCode { get; set; } } // Basic null-conditional public string? GetCity(Person? person) { // Returns null if person or Address is null return person?.Address?.City; } // With indexers public string? GetFirstPhone(Person? person) { // Returns null if person, PhoneNumbers is null, or empty return person?.PhoneNumbers?[0]; } // With method calls public int? GetNameLength(Person? person) { return person?.Name.Length; } // Combining operators public string GetCityOrDefault(Person? person) { return person?.Address?.City ?? "Unknown"; } // With arrays public string? GetFirstItem(string[]? items) { return items?[0]; } // With delegates public void InvokeCallback(Action? callback) { callback?.Invoke(); } // With events public event EventHandler? DataChanged; public void OnDataChanged() { DataChanged?.Invoke(this, EventArgs.Empty); } // Chaining multiple operations public void ChainedOperations(Person? person) { string? result = person? .Address? .City .ToUpper() .Substring(0, 3); } } ``` ## Null Handling Patterns Common patterns for safely handling null values. ```csharp #nullable enable using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; public class NullHandlingPatterns { // Null object pattern public interface ILogger { void Log(string message); } public class ConsoleLogger : ILogger { public void Log(string message) => Console.WriteLine(message); } public class NullLogger : ILogger { public void Log(string message) { } } // Guard clauses public void ProcessData(string? data) { if (data == null) { throw new ArgumentNullException(nameof(data)); } // data is non-null here Console.WriteLine(data.Length); } // Early return public string FormatData(string? data) { if (data == null) return string.Empty; return data.ToUpper(); } // TryGet pattern public bool TryGetValue(string key, [NotNullWhen(true)] out string? value) { // Simulate lookup if (key == "valid") { value = "Found"; return true; } value = null; return false; } public void UseTryGet() { if (TryGetValue("valid", out string? result)) { // result is guaranteed non-null here Console.WriteLine(result.Length); } } // MemberNotNull attribute private string? _cachedValue; [MemberNotNull(nameof(_cachedValue))] private void EnsureCacheLoaded() { _cachedValue ??= LoadValue(); } public void UseCache() { EnsureCacheLoaded(); // _cachedValue is guaranteed non-null Console.WriteLine(_cachedValue.Length); } // NotNullIfNotNull attribute [return: NotNullIfNotNull(nameof(input))] public string? Transform(string? input) { return input?.ToUpper(); } // AllowNull and DisallowNull private string _name = string.Empty; [AllowNull] public string Name { get => _name; set => _name = value ?? string.Empty; } private string LoadValue() => "loaded"; } ``` ## Nullable Annotations Attributes that provide additional null-state information to the compiler. ```csharp #nullable enable using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; public class NullableAnnotations { // NotNull - parameter won't be null when method returns public void Initialize([NotNull] ref string? value) { value ??= "default"; } // MaybeNull - return may be null despite non-nullable type [return: MaybeNull] public T GetDefaultValue<T>() { return default(T); } // DoesNotReturn - method never returns normally [DoesNotReturn] public void ThrowError(string message) { throw new System.InvalidOperationException(message); } public void UseDoesNotReturn(string? value) { if (value == null) { ThrowError("Value is null"); } // Compiler knows value is not null here System.Console.WriteLine(value.Length); } // MemberNotNullWhen private string? _data; [MemberNotNullWhen(true, nameof(_data))] private bool IsDataLoaded() { return _data != null; } public void UseData() { if (IsDataLoaded()) { // _data is guaranteed non-null System.Console.WriteLine(_data.Length); } } // NotNullIfNotNull [return: NotNullIfNotNull(nameof(source))] public string? ProcessString(string? source) { return source?.Trim(); } } ``` ## Migration Strategies Strategies for adopting nullable reference types in existing code. ```csharp // Gradual migration approach // File 1: Keep nullability disabled #nullable disable public class LegacyClass { public string Name { get; set; } // Implicitly nullable } #nullable restore // File 2: Enable nullability for new code #nullable enable public class ModernClass { public string Name { get; set; } = string.Empty; // Non-nullable public string? MiddleName { get; set; } // Explicitly nullable } #nullable restore // File 3: Suppress warnings during migration #nullable enable public class MigratingClass { // Suppress specific warnings #pragma warning disable CS8618 public string Name { get; set; } #pragma warning restore CS8618 // Or use ! operator temporarily public string GetName() => Name!; } ``` ## Working with External Libraries Handling nullability when working with libraries that don't use nullable annotations. ```csharp #nullable enable using System; public class ExternalLibraryHandling { // Assume external library method // public string GetData() { ... } // Defensive programming public void UseExternalLibrary() { // Treat return as potentially null string? data = GetExternalData(); if (data != null) { ProcessData(data); } } // Create wrappers public string? GetExternalDataSafe() { string result = GetExternalData(); return string.IsNullOrEmpty(result) ? null : result; } // Extension methods for safety public static class StringExtensions { public static string OrEmpty(this string? value) { return value ?? string.Empty; } public static bool IsNullOrEmpty( [NotNullWhen(false)] this string? value) { return string.IsNullOrEmpty(value); }
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기