C# Keywords Reference

C# keywords are words that have a special meaning to the compiler. They matter because keywords define the structure of your program: types, methods, loops, access rules, error handling, inheritance, pattern matching, asynchronous work, and more.

This reference explains the main keyword groups, how C# treats reserved and contextual keywords, and how to use them without accidentally fighting the compiler.

Overview: How C# Keywords Work

A keyword is part of the C# language grammar. When the compiler reads source code, it first breaks text into tokens such as identifiers, literals, punctuation, operators, and keywords. A token such as while is not just a variable name; it starts a loop statement. A token such as class starts a class declaration. That means keywords help the parser decide what kind of program structure it is reading.

C# has two broad kinds of keywords. Reserved keywords always have special meaning and normally cannot be used as ordinary variable, method, class, or property names. Examples include class, return, if, public, int, and new. Contextual keywords have special meaning only in certain positions. For example, var means implicit local typing in var count = 3;, but a class can technically have a member named var in another context. Other contextual keywords include async, await, record, init, where, yield, from, and select.

Some keywords describe built-in types. The keyword int is an alias for System.Int32, string is an alias for System.String, bool is an alias for System.Boolean, and object is an alias for System.Object. The compiler records the real .NET type in the compiled assembly, so using int or System.Int32 produces the same runtime type.

Other keywords affect metadata and runtime behavior. public, private, protected, and internal become accessibility information in the compiled assembly. static changes whether a member belongs to a type or to an instance. virtual, override, abstract, and sealed affect inheritance and method dispatch. try, catch, finally, and throw compile to structured exception handling instructions understood by the CLR.

You can escape a reserved keyword with the @ prefix, as in int @class = 5;. The variable name is still class in metadata, but the prefix tells the C# compiler to treat the token as an identifier. This is mainly useful when interoperating with code generated from another language or schema. In normal C# code, choose a clearer name instead.

Syntax

// Declaration and type keywords
accessModifier typeOrKeyword identifier = value;

// Control flow keywords
if (condition)
{
    statement;
}
else
{
    statement;
}

// Object-oriented keywords
accessModifier class TypeName : BaseType
{
    modifier returnType MemberName(parameters)
    {
        return value;
    }
}
Category Common keywords Purpose
Built-in types bool, byte, char, decimal, double, float, int, long, object, short, string, void Name common CLR types or absence of a return value.
Declarations class, struct, interface, enum, delegate, namespace, using Define program units and imports.
Access and modifiers public, private, protected, internal, static, readonly, const, abstract, sealed, virtual, override Control visibility, storage, inheritance, and reassignment.
Control flow if, else, switch, case, default, for, foreach, while, do, break, continue, return, goto Choose paths, repeat work, and exit statements or methods.
Exception handling try, catch, finally, throw Handle failures and guarantee cleanup.
Object and type operations new, this, base, is, as, typeof, sizeof, nameof Create objects, refer to current or base instances, and inspect types.
Async and iterators async, await, yield Build asynchronous methods and lazy sequences.
Generics and LINQ where, from, select, join, group, orderby, into, let Constrain generic type parameters and write query expressions.

Examples

Type, Declaration, and Control Flow Keywords

using System;

class Program
{
    static void Main()
    {
        const int passingScore = 70;
        int score = 82;
        string status;

        if (score >= passingScore)
        {
            status = "Pass";
        }
        else
        {
            status = "Retry";
        }

        Console.WriteLine(status);
    }
}

Output:

Pass

This small program uses using, class, static, void, const, int, string, if, and else. The compiler uses declaration keywords to build the program shape, type keywords to type-check variables, and control-flow keywords to produce branch instructions.

Inheritance and Modifier Keywords

using System;

abstract class Report
{
    public abstract string Title { get; }

    public virtual void Print()
    {
        Console.WriteLine("Report: " + Title);
    }
}

sealed class SalesReport : Report
{
    public override string Title => "Quarterly Sales";

    public override void Print()
    {
        base.Print();
        Console.WriteLine("Status: complete");
    }
}

class Program
{
    static void Main()
    {
        Report report = new SalesReport();
        report.Print();
    }
}

Output:

Report: Quarterly Sales
Status: complete

abstract prevents direct construction of Report and requires derived classes to provide the Title. virtual allows replacement behavior, override supplies it, base calls the parent implementation, and sealed prevents further inheritance from SalesReport.

Pattern, Null, and Type Operation Keywords

using System;

class Program
{
    static void Main()
    {
        object value = "CSharp";

        if (value is string text)
        {
            Console.WriteLine($"Name: {text}");
            Console.WriteLine($"Type: {typeof(string).Name}");
            Console.WriteLine($"Identifier: {nameof(text)}");
        }

        string? optional = value as string;
        Console.WriteLine(optional?.Length ?? 0);
    }
}

Output:

Name: CSharp
Type: String
Identifier: text
6

The object keyword names the base type of all .NET objects. The is keyword checks the runtime type and introduces a pattern variable. typeof gets a System.Type, nameof produces an identifier string at compile time, and as performs a safe reference conversion that returns null when it fails.

Exception and Iterator Keywords

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        try
        {
            foreach (int number in TakePositive(3))
            {
                Console.WriteLine(number);
            }
        }
        catch (ArgumentOutOfRangeException ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            Console.WriteLine("Done");
        }
    }

    static IEnumerable<int> TakePositive(int count)
    {
        if (count < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(count));
        }

        for (int i = 1; i <= count; i++)
        {
            yield return i;
        }
    }
}

Output:

1
2
3
Done

This example combines try, catch, finally, throw, foreach, for, and yield. The iterator method does not build a list. The compiler transforms it into a state machine that produces one value at a time as the foreach loop asks for the next item.

How Keywords Work Step by Step

  1. The compiler tokenizes the source and recognizes reserved keywords immediately.
  2. It parses those tokens according to grammar rules. For example, if must be followed by a Boolean expression in parentheses and then a statement or block.
  3. It resolves contextual keywords by position. await is meaningful inside an async method, while where is meaningful in generic constraints and LINQ queries.
  4. It binds names and types. Type keywords become CLR types, modifiers become metadata, and control-flow keywords become branches, calls, state machines, or exception-handling regions.
  5. The generated assembly contains IL and metadata. At runtime, the CLR executes the IL; it does not keep reading C# keywords from the original source file.

This explains why keywords are mostly compile-time concepts. The runtime cares about the result: types, methods, fields, branches, exception tables, and metadata. Features such as async and yield are especially important because the compiler rewrites the method into a generated state machine before the CLR runs it.

Common Mistakes

Using a Reserved Keyword as a Normal Name

int class = 5;
Console.WriteLine(class);

This does not compile because class is reserved. The escape prefix works, but it should usually be a last resort:

int @class = 5;
Console.WriteLine(@class);

Output:

5

Prefer a meaningful name such as classNumber, courseName, or category. Escaped identifiers are most useful when matching an external field name exactly.

Forgetting That Contextual Keywords Depend on Position

using System;

class Program
{
    static void Main()
    {
        var count = 3;
        string var = "label";

        Console.WriteLine(count);
        Console.WriteLine(var);
    }
}

Output:

3
label

The first var asks the compiler to infer the local variable type from 3. The second var is an identifier because it appears where a variable name belongs after the declared type string. This compiles, but it is confusing and should be avoided.

Using return Where yield return Is Required

static IEnumerable<int> Numbers()
{
    return 1;
}

This does not compile because a method returning IEnumerable<int> must return a sequence object, not a single int. If you want an iterator, use yield return:

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        foreach (int number in Numbers())
        {
            Console.WriteLine(number);
        }
    }

    static IEnumerable<int> Numbers()
    {
        yield return 1;
        yield return 2;
    }
}

Output:

1
2

Best Practices

  • Do not use escaped keywords as identifiers unless you are matching external generated names.
  • Prefer readable names over clever contextual-keyword tricks, even when the compiler allows them.
  • Use the C# aliases such as int, string, and bool consistently in normal source code; use CLR type names when reflecting over types or following a local style guide.
  • Keep modifier order conventional: accessibility first, then keywords such as static, abstract, virtual, override, or readonly.
  • Use var when the type is obvious from the right side, and use an explicit type when it improves readability.
  • Avoid goto in ordinary application code; structured keywords such as if, switch, loops, break, and continue are clearer.
  • Use nameof instead of string literals for parameter names and member names that must track refactoring.
  • Remember that async, await, yield, LINQ keywords, and pattern keywords can cause compiler rewrites that affect allocation, control flow, and debugging.

Practice Exercises

  1. Write a small program that uses const, if, else, and return to print whether a temperature is freezing.
  2. Create a base class with a virtual method and a derived class with an override. Call it through a variable typed as the base class.
  3. Write an iterator method that uses yield return to produce the even numbers from 2 through 10.

Summary

  • C# keywords are compiler-recognized words that define program structure and behavior.
  • Reserved keywords normally cannot be used as identifiers; contextual keywords are special only in certain positions.
  • Type keywords such as int and string are aliases for real CLR types.
  • Modifier keywords affect accessibility, inheritance, metadata, storage, and dispatch.
  • Control-flow and exception keywords compile into branches, loops, returns, and exception-handling regions.
  • Some keywords, especially async and yield, cause the compiler to generate state-machine code.
  • Use keywords clearly and conventionally; code that merely compiles is not always code that is easy to read.