Understanding Variable Scope and Access Modifiers in C#

Variable Scope in C#

Variable scope determines where a variable can be accessed within your code. C# supports several distinct types of scope.

Local Variables

Local variables are declared inside methods, constructors, properties, or any nested code blocks. Their scope is limited to the enclosing code block defined by curly braces {}.

namespace ScopeDemo
{
    internal class Example
    {
        void Calculate()
        {
            int counter = 100;
            if (true)
            {
                Console.WriteLine(counter);
            }
            Console.WriteLine(counter);
        }
        // counter cannot be accessed here
    }
}

Local variables are destroyed once execution leaves their containing block and are recreated each time the block is entered.

Block Scope

Block scope applies to variables declared within any code block delimited by curly braces. This is particularly relevant in conditional statements and loops.

void ProcessData()
{
    if (true)
    {
        int tempValue = 200;
        Console.WriteLine(tempValue);
    }
    // Error: tempValue is not accessible here
    Console.WriteLine(tempValue);
}

Method Parameter Scope

Method parameters are declared in the method signature and receive values passed to the method. Their scope extends throughout the entire method body.

void DisplayMessage(string message)
{
    Console.WriteLine(message);
}

// message cannot be accessed outside this method

Class-Level Variables

While C# lacks true global variables, class fields serve a similar purpose. Instance fields exist throughoutt the class and are accessible to all methods within that class.

class DataManager
{
    int sharedValue = 300;
}

Static Variable Scope

Static variables belong to the class itself rather than individual instances. They persist for the application's lifetime and are shared across all instances of the class.

class Configuration
{
    static int maxRetries = 5;
}

Instance Variables vs Static Variables

Instance Variables

  • Ownership: Each object receives its own copy of the variable
  • Lifetime: Tied to the object instance; created when the object is instantiated, destroyed when garbage collected
  • Storage: Stored on the heap as part of the object
  • Access: Accessed through an object instance

Static Variables

  • Ownership: Belong to the class itself; only one copy exists regardless of instance count
  • Lifetime: Exist from class load time until application terminasion
  • Storage: Stored in the method area alongside class metadata
  • Access: Can be accessed directly via the class name

Instance variables suit scenarios where each object needs independent state. Static variables work well for shared configuration, utilities, and frequently accessed constants.

using System;

namespace AdvancedDemo
{
    internal class Counter
    {
        static public int totalCount;

        public void Increment()
        {
            totalCount++;
        }

        public int GetCount()
        {
            return totalCount;
        }
    }
}

// Usage
Counter first = new Counter();
first.Increment();
first.Increment();
Console.WriteLine(first.GetCount());

Counter second = new Counter();
second.Increment();
second.Increment();
Console.WriteLine(second.GetCount());

Both instances reference the same totalCount, demonstrating how static variables provide shared state across all instances.

Recursive Directory Traversal

The following example demonstrates recursive directory traversal, printing filename, extension, and file size.

using System;
using System.IO;

namespace FileSystemWalker
{
    internal class FolderScanner
    {
        public void Scan(string targetPath)
        {
            DirectoryInfo dir = new DirectoryInfo(targetPath);

            if (dir.Exists)
            {
                FileInfo[] files = dir.GetFiles();
                foreach (FileInfo f in files)
                {
                    Console.WriteLine("Name: {0}, Extension: {1}, Size: {2} bytes",
                        f.Name, f.Extension, f.Length);
                }

                DirectoryInfo[] subfolders = dir.GetDirectories();
                foreach (DirectoryInfo sub in subfolders)
                {
                    Scan(sub.FullName);
                }
            }
            else
            {
                Console.WriteLine("Path '{0}' not found.", targetPath);
            }
        }
    }
}
FolderScanner scanner = new FolderScanner();
string path = @"D:\Projects\Data";
scanner.Scan(path);

Note that C# allows both system-thrown and developer-thrown exceptions. Exception handling can be added to handle UnauthorizedAccessException or DirectoryNotFoundException for robust operation.

Access Modifiers in C#

C# provides five access modifiers, offering more granular control than languages like Java:

Modifier Description
public Accessible from any code, anywhere
private Accessible only within the declaring class
protected Accessible within the class and its derived classes
internal Accessible within the same assembly only
protected internal Accessible within the same asembly or any derived class

Public

Members declared as public have no access restrictions. They can be accessed from any code in the same project or external projects.

Private

Private members are hidden from everything outside the declaring class. Even derived classes cannot access them directly.

Protected

Protected members are visible to the declaring class and all classes that inherit from it, regardless of assembly location.

Internal

Internal members are accessible only within the same assembly. Code from other assemblies cannot access these members.

Protected Internal

This combines both protected and internal. Members are accessible from derived classes or any code within the same assembly.

Practical Comparison

namespace ModifierDemo
{
    internal class Parent
    {
        public int publicField;
        protected int protectedField;
        private int privateField;

        public void PublicMethod()
        {
            Console.WriteLine("Public method accessible");
        }

        protected void ProtectedMethod()
        {
            Console.WriteLine("Protected method accessible");
        }

        private void PrivateMethod()
        {
            Console.WriteLine("Private method accessible");
        }
    }

    internal class Child : Parent
    {
        public void AccessMembers()
        {
            publicField = 10;
            protectedField = 20;
            // privateField = 30; // Error: inaccessible

            PublicMethod();
            ProtectedMethod();
            // PrivateMethod(); // Error: inaccessible
        }
    }
}
Child instance = new Child();
instance.publicField = 10;
// instance.protectedField = 20; // Error: external code cannot access

instance.PublicMethod();
// instance.ProtectedMethod(); // Error: external code cannot access

The distinction is clear: public is unrestricted, protected extends access to subclasses, and private restricts access entirely to the declaring class.

Tags: C# variable scope Access Modifiers Static Variables Recursion

Posted on Tue, 16 Jun 2026 17:20:28 +0000 by DarkJamie