← Open in the full interactive course (progress tracking, search & more)

A naming convention isn't decoration — it's a shared agreement that lets any C# developer read your code and immediately know what kind of thing they're looking at.

Look at these two lines side by side:

customer.Name
customer._name

An experienced C# developer glances at these and instantly knows something you didn't have to say out loud: the first is a public property — probably safe to read from anywhere. The second is a private field — internal state that shouldn't be touched from outside the class at all. Nobody explained that; the naming itself carried the information.

That's the entire point of naming conventions. .NET has a well-established, near-universal set of casing rules for types, methods, parameters, and fields. They're not arbitrary — every experienced C# developer already knows them, which means following them lets your code communicate instantly to anyone who reads it, and breaking them creates constant tiny friction ("wait, is this a field or a property?") for every person who touches your code afterward.

In this lesson, you'll learn the standard .NET naming conventions — PascalCase, camelCase, the underscore-prefix convention for private fields, and the I prefix for interfaces — and why consistency here matters more than personal preference.

What Is It?

The Simple Explanation

A naming convention is an agreed-upon pattern for how to capitalize and format identifiers — the names you give to classes, methods, variables, and everything else in your code — so that the shape of a name tells you something about what it is, before you even read its meaning.

The Technical Definition — The .NET Convention Table

Microsoft's official .NET naming guidelines define two core casing styles and where each applies:

Element Convention Example
Class / record / structPascalCaseOrderProcessor, Expense
InterfacePascalCase, I prefixIPayable, IComparable
MethodPascalCaseCalculateTotal()
PropertyPascalCaseFirstName, IsActive
Public/const field, enum memberPascalCaseMaxRetries, DayOfWeek.Monday
Local variablecamelCaseorderTotal, customerName
Method parametercamelCasevoid Deposit(decimal amount)
Private field_camelCaseprivate readonly ILogger _logger;

Two casing styles, applied consistently by role — that's essentially the whole system. The trick is knowing which role each identifier plays.

Why Does It Exist?

The Problem

Imagine a codebase where every developer casing their own way: one person writes calculate_total(), another writes CalculateTotal(), a third writes calculateTotal() — all in the same file, all doing similar things. Now every time you read a name, you have to stop and mentally translate it, and you get zero information from the casing itself about whether it's a type, a method, or a local variable. Worse, inconsistency makes typos and mismatches far more likely — is it userName or userName (identical) or UserName (a different symbol entirely, since C# is case-sensitive)?

The Solution

By agreeing on one convention per role — types and members get PascalCase, locals and parameters get camelCase, private fields get an underscore prefix — the .NET community turned casing into free information. The moment you see _total, you know it's a private field before you've even read what it holds. The moment you see IDisposable, you know it's an interface. This isn't enforced by the compiler (C# will happily compile code that ignores all of it) — it's a social convention, maintained because everyone benefits when everyone follows it.

Big Picture

A single class shows every convention working together at once:

public interface IPayable                    // I + PascalCase → interface
{
    decimal CalculatePay();                   // PascalCase → method
}

public class HourlyEmployee : IPayable        // PascalCase → class
{
    private readonly decimal _hourlyRate;     // _camelCase → private field

    public string Name { get; set; } = "";    // PascalCase → property
    public decimal HoursWorked { get; set; }  // PascalCase → property

    public HourlyEmployee(string name, decimal hourlyRate)   // parameters: camelCase
    {
        Name = name;
        _hourlyRate = hourlyRate;
    }

    public decimal CalculatePay()
    {
        decimal grossPay = HoursWorked * _hourlyRate;    // local variable: camelCase
        return grossPay;
    }
}

Reading this without any prior context: IPayable is unmistakably an interface, HourlyEmployee is unmistakably a type, _hourlyRate is unmistakably private state, and grossPay is unmistakably a throwaway local — all from casing alone.

How It Works

Rule 1 — Types get PascalCase

Classes, records, structs, and enums are all capitalized at the start of every word: ExpenseTracker, OrderStatus, Point. This includes the file name too, by convention — ExpenseTracker.cs holds class ExpenseTracker.

Rule 2 — Interfaces get PascalCase plus an I prefix

public interface IComparable
public interface INotifyPropertyChanged
public interface IPayable

This is one of the few purely visual markers in the whole system, and .NET developers rely on it constantly — the instant you see a type starting with a capital I followed by another capital letter, you know it's a contract, not a concrete implementation.

Rule 3 — Public members (methods, properties) get PascalCase

public string FirstName { get; set; }   // property
public void Deposit(decimal amount)     // method

Rule 4 — Locals and parameters get camelCase

void Deposit(decimal amount)         // parameter: camelCase
{
    decimal newBalance = Balance + amount;   // local variable: camelCase
    Balance = newBalance;
}

Rule 5 — Private fields get an underscore prefix, then camelCase

public class BankAccount
{
    private decimal _balance;              // private field: _camelCase
    private readonly string _accountId;    // private readonly field: _camelCase

    public decimal Balance => _balance;    // exposed via a PascalCase property
}

This is arguably the single most valuable convention in the whole list, because it instantly disambiguates the extremely common pattern of a private field backing a public property with a nearly identical name — _balance vs Balance — without any risk of confusing the two.

Rule 6 — Constants and enum members get PascalCase too

public const int MaxRetryAttempts = 3;

public enum OrderStatus
{
    Pending,
    Shipped,
    Delivered,
    Cancelled
}

Note that this differs from some other languages (like C or older C++ style guides), which often use ALL_CAPS for constants — that convention doesn't apply in idiomatic C#.

Simple Example

Here's the same tiny class written first with inconsistent, ad-hoc casing, then following the standard convention:

//  Before — inconsistent, no visual cues
public class person
{
    public string name;
    public int Age;
    public string get_greeting() { return "Hi, " + name; }
}

//  After — follows convention, self-explanatory
public class Person
{
    private string _name;

    public string Name { get; set; }
    public int Age { get; set; }

    public string GetGreeting() => $"Hi, {Name}";
}

Notice how, in the "after" version, you can tell at a glance which members are public API (Name, Age, GetGreeting) versus private internal state (_name) — purely from casing, without reading a single access modifier.

Real-World Example

In a team setting, naming conventions are usually enforced automatically through an .editorconfig file checked into the repository, which most IDEs read and flag violations against in real time:

# .editorconfig (excerpt)
dotnet_naming_rule.private_fields_underscore_camel.severity = warning
dotnet_naming_rule.private_fields_underscore_camel.symbols = private_fields
dotnet_naming_rule.private_fields_underscore_camel.style = underscore_camel_case

dotnet_naming_rule.interfaces_prefix_i.severity = warning
dotnet_naming_rule.interfaces_prefix_i.symbols = interfaces
dotnet_naming_rule.interfaces_prefix_i.style = pascal_case_i_prefix

When a developer opens a pull request that names a private field hourlyRate instead of _hourlyRate, the IDE underlines it and the build can even fail in CI. This isn't bureaucracy for its own sake — it's what lets a team of developers who've never met read each other's code without a single conversation about "how do you name things here?"

Analogy

Traffic signs and road markings

A red octagon always means "stop," everywhere, without needing to read the word. A dashed yellow line always means "you may pass here"; a solid one means "you may not." Drivers don't relearn these rules at every intersection — the shape and color already carry the meaning, universally.

Naming conventions do the same job for code. IPayable is a red octagon that says "interface" before you've read another word. _hourlyRate is a solid line that says "private, don't touch from outside." You're not memorizing a new rule for every codebase you join — the convention already told you.

Common Confusion

1. camelCase vs PascalCase — what's the actual difference?

Both capitalize the first letter of every word except the first. camelCase keeps that very first letter lowercase (orderTotal); PascalCase capitalizes it too (OrderTotal). Everything else is identical.

2. Is the underscore prefix required by the compiler?

No — it's purely convention, not a language rule. private decimal balance; compiles just as fine as private decimal _balance;. The prefix exists purely to help human readers, and its value comes entirely from everyone agreeing to use it.

3. "Hungarian notation" is a different, mostly outdated convention

Older code (especially from the pre-.NET Windows/COM era) sometimes prefixes variables with a type hint, like strName or iCount. Modern C# strongly discourages this — the compiler's static typing already tells you the type, and IDEs show it on hover, so encoding it into the name just adds noise (and gets wrong the moment the type changes but nobody updates the name).

Common Mistakes

Mistake 1 — Mixing casing styles within the same codebase

One class uses _name for private fields, another uses m_name, a third uses plain name. Every reader has to relearn the rules per file.

Pick the standard .NET convention and apply it uniformly — ideally enforced by an .editorconfig so it isn't left to memory.

Mistake 2 — Leftover Hungarian notation

string strFirstName;, int iTotalCount; — encodes type information the compiler and IDE already give you for free.

Just name it for what it represents: firstName, totalCount.

Mistake 3 — Forgetting the I prefix on interfaces

interface Payable { ... } — looks identical to a class name, and readers have to open the declaration to find out it's actually a contract.

interface IPayable { ... } — unmistakable at a glance.

Mistake 4 — Single-letter or overly abbreviated names outside tight loops

decimal amt; string custNm; — saves a few keystrokes at the cost of forcing every future reader to decode an abbreviation.

Reserve single letters for genuinely tight, obvious scopes (for (int i = 0; ...)). Everywhere else, spell it out: amount, customerName.

When Should I Use It?

Mental Model

PascalCase = types and anything public (classes, methods, properties).
camelCase = anything local (locals, parameters).
_camelCase = private fields — the underscore is the "keep out" sign.
I + PascalCase = interfaces, always.

Remember:
· Casing is free information — use it consistently and readers get it for free too.
· The compiler doesn't enforce any of this; your team's consistency does.
· When in doubt, match the existing codebase over your personal preference.

Key Takeaway


Check Your Understanding

You've learned the standard .NET casing rules for every kind of identifier. Let's check they'll stick when you're writing real code.

1. Which of these correctly follows standard .NET naming conventions for a private field and its backing property?

Show answer

Correct: B

Why B is correct: The private field uses the underscore-prefixed camelCase convention (_balance), and the public property uses PascalCase (Balance) — exactly matching the standard .NET pattern for a field-backed property.

Why A is incorrect: The casing is reversed — the private field is capitalized like a public member, and the public property is lowercase like a local.

Why C is incorrect: The private field is missing its underscore prefix, making it visually indistinguishable from a local variable.

Why D is incorrect: m_Balance is a Hungarian-notation-style prefix from older non-.NET conventions, not the idiomatic .NET underscore style, and the public property is incorrectly lowercase.

Reinforcement: Private fields get _camelCase; public properties get PascalCase — this pairing is one of the most common patterns you'll write.

2. Which name correctly follows convention for a new interface describing something that can be validated?

Show answer

Correct: C

Why C is correct: Interfaces in .NET use PascalCase with a leading capital I, which is exactly what IValidatable does.

Why A is incorrect: This is valid PascalCase, but without the I prefix it looks identical to a class name, losing the instant "this is an interface" signal.

Why B is incorrect: Lowercase-first casing (camelCase) is reserved for locals and parameters, never for type names like interfaces.

Why D is incorrect: Spelling out "Interface" in the name is redundant and not the .NET convention — the I prefix already communicates that.

Reinforcement: Interfaces always combine PascalCase with a leading I.

3. Inside a method body, which of these is the correctly-cased local variable declaration?

Show answer

Correct: C

Why C is correct: Local variables use camelCase — the first word lowercase, subsequent words capitalized — exactly as in totalPrice.

Why A is incorrect: PascalCase is reserved for types and public members, not local variables.

Why B is incorrect: The underscore prefix is reserved for private fields at the class level, not local variables declared inside a method body.

Why D is incorrect: Snake_case (words separated by underscores) is not the standard C# convention for any identifier category.

Reinforcement: Locals and parameters both use camelCase, with no underscore prefix.

4. Why does the .NET naming convention specifically distinguish private fields (_balance) from public properties (Balance) with different casing, rather than letting them look the same?

Show answer

Correct: B

Why B is correct: This distinction resolves a very common naming collision — a private field and its public property often represent "the same" piece of data — while letting the reader instantly tell which one is safe to use from outside the class and which is internal implementation detail.

Why A is incorrect: C# would compile balance and Balance as distinct symbols regardless (C# is case-sensitive); the compiler has no such restriction.

Why C is incorrect: There's no length rule involved — the underscore is a fixed prefix, not a length constraint.

Why D is incorrect: The convention exists specifically to give readers a fast, reliable visual signal about scope and intent — that's a concrete, practical benefit, not an arbitrary rule.

Reinforcement: Consistent, role-based casing turns a name's shape into free information about what it represents.

You now read and write C# names the way the rest of the .NET community does — one less thing to think about, every single time you open a file.


dotnetmadeeasy.com — Learn C# and .NET, the right way.