0Pricing
C# Academy · บทเรียน

คำอธิบายประกอบ: ?, !, MaybeNull และ NotNull

ใช้ ? สำหรับชนิดที่เป็น nullable ใช้ ! เพื่อระบุว่าไม่เป็น null และใช้แอตทริบิวต์อย่าง MaybeNull กับ NotNullWhen เพื่อวิเคราะห์การไหลของข้อมูลอย่างแม่นยำ

คำอธิบายประกอบ: ?, !, MaybeNull และ NotNull เป็นบทเรียน C# Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C# Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน

นอกเหนือจาก ? และ !: แอตทริบิวต์การอนุญาตค่าว่าง

คำกำกับ ? พื้นฐานและการตรวจสอบค่าว่างรองรับกรณีส่วนใหญ่ แต่รูปแบบบางอย่างต้องใช้คำกำกับที่สื่อความหมายได้ละเอียดขึ้น เนมสเปซ System.Diagnostics.CodeAnalysis มีแอตทริบิวต์ที่ช่วยให้คอมไพเลอร์เข้าใจการไหลของสถานะค่าว่างได้ลึกยิ่งขึ้น

MaybeNull และ NotNull

[MaybeNull] แจ้งคอมไพเลอร์ว่าชนิดค่าที่ประกาศว่าไม่เป็นค่าว่างอาจเป็นค่าว่างได้จริงขณะทำงาน (เช่น ในเมธอดชนิดข้อมูลทั่วไป) ส่วน [NotNull] รับรองว่าพารามิเตอร์ที่อาจเป็นค่าว่างจะไม่เป็นค่าว่างหลังการเรียกใช้

using System.Diagnostics.CodeAnalysis;

// [MaybeNull]: return might be null even though T is non-nullable
[return: MaybeNull]
public T Find<T>(int id)
{
    // Returns default(T) which is null for reference types
    return _cache.TryGetValue(id, out var val) ? val : default!;
}

// [NotNull]: after this call, output is guaranteed non-null
public static void EnsureNotNull<T>(
    [NotNull] ref T? value,
    T defaultValue) where T : class
{
    value ??= defaultValue;
}

NotNullWhen: ไม่เป็นค่าว่างตามเงื่อนไข

[NotNullWhen(true)] แจ้งคอมไพเลอร์ว่าพารามิเตอร์เอาต์พุตไม่เป็นค่าว่างเมื่อเมธอดคืนค่า true นี่คือวิธีที่กำกับรูปแบบ TryParse มาตรฐาน

using System.Diagnostics.CodeAnalysis;

public static bool TryParseEmail(
    string? input,
    [NotNullWhen(true)] out string? email)
{
    if (input?.Contains('@') == true)
    {
        email = input.Trim().ToLower();
        return true;
    }
    email = null;
    return false;
}

// Usage — no warning after true check:
if (TryParseEmail(raw, out var email))
    Console.WriteLine(email.Length); // safe — email is non-null here

MaybeNullWhen: เป็นค่าว่างตามเงื่อนไข

[MaybeNullWhen(false)] เป็นกรณีตรงข้าม โดยเอาต์พุตอาจเป็นค่าว่างเมื่อเมธอดคืนค่า false ใช้ในรูปแบบพจนานุกรมของ TryGetValue

// This is how Dictionary<K,V>.TryGetValue is annotated:
public bool TryGetValue(
    TKey key,
    [MaybeNullWhen(false)] out TValue value)
{ ... }

// Usage:
if (!dict.TryGetValue("key", out var value))
    return; // early return — value is null in this branch

Console.WriteLine(value.Length); // safe after the guard

AllowNull และ DisallowNull

[AllowNull] บนคุณสมบัติที่ไม่เป็นค่าว่าง อนุญาตให้ผู้เรียกส่งค่าว่างให้คุณสมบัตินั้นได้ (เช่น ตัวตั้งค่าที่แปลงค่าว่างเป็นสตริงว่าง) ส่วน [DisallowNull] จะห้ามใช้ค่าว่างกับชนิดที่อนุญาตให้เป็นค่าว่าง

public class Config
{
    private string _name = "";

    // Allow setting null (setter converts null -> empty)
    [AllowNull]
    public string Name
    {
        get => _name;
        set => _name = value ?? "";
    }

    // Getter always returns non-null: fine
    // Setter accepts null: [AllowNull] tells compiler that's OK
}

NotNullIfNotNull: การส่งต่อสถานะค่าว่าง

[NotNullIfNotNull(paramName)] ระบุว่า หากพารามิเตอร์ X ไม่เป็นค่าว่าง ค่าที่คืนกลับก็จะไม่เป็นค่าว่างด้วย มีประโยชน์สำหรับฟังก์ชันแปลงข้อมูล

[return: NotNullIfNotNull(nameof(value))]
public static string? Normalize(string? value)
{
    return value?.Trim().ToLower();
}

// Usage:
string  name = "  Alice  ";
string  norm1 = Normalize(name)!; // guaranteed non-null
string? raw   = GetRaw();
string? norm2 = Normalize(raw);   // still nullable (raw might be null)

DoesNotReturn

[DoesNotReturn] ใช้กำกับเมธอดที่ขว้างข้อยกเว้นเสมอ คอมไพเลอร์จึงทราบว่าโค้ดหลังการเรียกใช้ไม่สามารถทำงานได้ และไม่แสดงคำเตือนเกี่ยวกับค่าว่างที่ไม่เกี่ยวข้อง

using System.Diagnostics.CodeAnalysis;

[DoesNotReturn]
public static void ThrowNotFound(int id)
    => throw new KeyNotFoundException($"Entity {id} not found");

// Usage — no null warning after the call:
var order = _db.Orders.Find(id);
if (order is null) ThrowNotFound(id);

Console.WriteLine(order.Id); // no CS8602 — compiler knows ThrowNotFound threw

MemberNotNull: เงื่อนไขหลังการทำงานของฟิลด์

[MemberNotNull] แจ้งคอมไพเลอร์ว่าเมธอดรับรองว่าฟิลด์บางรายการจะไม่เป็นค่าว่างหลังจากเมธอดทำงานเสร็จ มีประโยชน์สำหรับตัวช่วยเริ่มต้นค่าแบบเลื่อนเวลา

public class DataLoader
{
    private string? _data;

    [MemberNotNull(nameof(_data))]
    private void EnsureLoaded()
    {
        if (_data is null)
            _data = LoadFromFile();
    }

    public string GetData()
    {
        EnsureLoaded();
        return _data; // no CS8603 — compiler knows _data is set
    }
}

การรวมแอตทริบิวต์

สามารถรวมแอตทริบิวต์เข้าด้วยกันเพื่อสร้างสัญญา API ที่แม่นยำได้ ต่อไปนี้คือตัวช่วย Guard แบบต่อคำสั่งที่รวมแอตทริบิวต์หลายรายการเข้าด้วยกัน

public static class Guard
{
    [return: NotNull]
    public static T NotNull<T>(
        [NotNull][AllowNull] T? value,
        [CallerArgumentExpression(nameof(value))] string? name = null)
        where T : class
    {
        ArgumentNullException.ThrowIfNull(value, name);
        return value;
    }
}

// Usage:
var product = Guard.NotNull(await _repo.FindAsync(id));
Console.WriteLine(product.Name); // no warning

ตัวดำเนินการจัดการค่าว่าง: ?. ?? ??=

ตัวดำเนินการตรวจสอบค่าว่างแบบมีเงื่อนไข (?.) ตัวดำเนินการรวมค่าทดแทนเมื่อเป็นค่าว่าง (??) และการกำหนดค่าแบบรวมค่าทดแทนเมื่อเป็นค่าว่าง (??=) ช่วยเขียนโค้ดที่ปลอดภัยจากค่าว่างได้กระชับ โดยไม่ต้องใช้การตรวจสอบด้วย if ที่ยืดยาว

string? name = GetName();

// ?. safe navigation
int? len = name?.Length;
string? upper = name?.ToUpper().Trim();

// ?? default value
string display = name ?? "Anonymous";

// ??= lazy init
name ??= "Default";

// Chaining
string result = user?.Profile?.DisplayName ?? user?.Name ?? "Guest";

ตรวจสอบความเข้าใจอย่างรวดเร็ว

[NotNullWhen(true)] บนพารามิเตอร์เอาต์พุตบอกอะไรแก่คอมไพเลอร์

สรุป: คำกำกับการอนุญาตค่าว่าง

ประเด็นสำคัญ:

  • [NotNullWhen(true)]: รูปแบบ TryParse — ไม่เป็นค่าว่างเมื่อคืนค่า true
  • [MaybeNull]: ชนิดที่ไม่เป็นค่าว่างอาจยังคืนค่าว่างได้ (ค่าเริ่มต้นของชนิดข้อมูลทั่วไป)
  • [DoesNotReturn]: เมธอดขว้างข้อยกเว้นเสมอ — โค้ดหลังจากนั้นไม่สามารถทำงานได้
  • [MemberNotNull]: เมธอดรับรองว่าฟิลด์ถูกกำหนดค่าหลังจากทำงานเสร็จ
  • ?., ??, ??=: การนำทางและกำหนดค่าเริ่มต้นที่ปลอดภัยจากค่าว่างอย่างกระชับ

คำถามที่พบบ่อย

บทเรียน “คำอธิบายประกอบ: ?, !, MaybeNull และ NotNull” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “คำอธิบายประกอบ: ?, !, MaybeNull และ NotNull” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C# Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “คำอธิบายประกอบ: ?, !, MaybeNull และ NotNull”

ใช้ ? สำหรับชนิดที่เป็น nullable ใช้ ! เพื่อระบุว่าไม่เป็น null และใช้แอตทริบิวต์อย่าง MaybeNull กับ NotNullWhen เพื่อวิเคราะห์การไหลของข้อมูลอย่างแม่นยำ คุณปฏิบัติ C# Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C# Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน C# Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “คำอธิบายประกอบ: ?, !, MaybeNull และ NotNull” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน C# Academy นี้ได้ไหม

ได้ บทเรียน C# Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การเปิดใช้และทำความเข้าใจ NRT
  2. คำอธิบายประกอบ: ?, !, MaybeNull และ NotNull
  3. ตัวดำเนินการเงื่อนไข Null และรวมค่า Null
  4. การย้ายโค้ดเบสไปยัง NRT
← กลับไปที่ C# Academy