C# Class Structure: A Developer's Memory Guide

C# Class Structure: A Developer's Memory Guide

I always have that moment of "I think I know how to code... but do I remember the class structure order?" So I wrote this down for myself.

This is the mental model I use when I want a clean, readable C# class file.

Why bother with order?

It helps with:

  1. Readability: easier to scan
  2. Maintenance: less time hunting for things
  3. Professionalism: it just looks cleaner

The UNC-SFICN mnemonic

To remember the order, I use "UNC-SFICN" (I say it like "Uncle Sficin" in my head):

U - Usings - File imports/references

N - Namespace declaration

C - Class declaration - Including attributes and XML documentation

S - Statics - Constants, Static Fields, Static Properties, Static Events and Static Methods

F - Fields (instance fields only)

I - Initialization (constructors)

C - Class behaviors - Instance Properties, Events and Methods

N - Nested types (inner classes, structs, interfaces, enums)

Quick breakdown below.

U - Usings

Start with all your using directives, usually system first, then your own:

using System;
using System.Collections.Generic;
using System.Linq;
using MyCompany.Services;
using MyCompany.Utilities;

N - Namespace Declaration

Declare your namespace to organize your types:

namespace MyCompany.Core
{
    // Everything else goes inside here
}

C - Class Declaration

Define your class and any documentation/attributes:

/// docs
public class DataManager : IDataProcessor
{
    // Class members go here
}

S - Statics

Static members up top (constants, fields, properties, methods):

// Constants
public const string DefaultConnectionString = "";

// Static fields
private static readonly HttpClient _httpClient = new HttpClient();

// Static properties
public static TimeSpan DefaultTimeout { get; set; } = TimeSpan.FromSeconds(30);

// Static methods
public static bool IsValidConfiguration(Configuration config)
{
    // method logic here
}

F - Fields

Instance fields next:

// Private fields
protected readonly ILogger _logger;
private readonly IRepository _repository;
private bool _isInitialized;

I - Initialization

Constructors and setup:

// Main constructor
public DataManager(IRepository repository, ILogger logger)
{
    // Initialization logic here
}

// Parameterless constructor
public DataManager() : this(new DefaultRepository(), LogManager.GetCurrentClassLogger())
{
}

C - Class Behaviors

Instance properties, events, and methods:

// Properties
public bool IsConnected { get; private set; }
public ConnectionStatus Status => _isInitialized ? ConnectionStatus.Ready : ConnectionStatus.Pending;

// Methods
public async Task<DataResult> ProcessDataAsync(DataRequest request)
{
    // method logic here
}

N - Nested Types

Nested enums/classes/interfaces at the bottom:

// Nested enum
public enum ConnectionStatus
{
    Pending,
    Ready,
    Failed
}

// Nested class
private class DataRequestValidator
{
    public bool Validate(DataRequest request)
    {
        // method logic here
    }
}

Access Modifier Order

One last thing I try to keep straight: order members from most accessible to least accessible:

  1. public - Accessible by any code
  2. protected - Accessible by the class and its subclasses
  3. internal - Accessible within the same assembly
  4. protected internal - Accessible by the assembly or derived classes
  5. private protected - Accessible by derived classes within the same assembly
  6. private - Accessible only within the declaring class

Hopefully this helps me (and you) keep things tidy.

Comments

No comments yet.

Add a comment