Configuration class in C#, reading an INI file This class will load a file with INI configuration elements and cache them, it then has a method to get an item from the cache if it needs to.

internal class ConfigHandler
{
    private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

    private Dictionary&lt;string, string&gt; _configItems;

    public ConfigHandler()
    {
        _configItems = new Dictionary&lt;string, string&gt;();
        LoadConfig("resources/config/pitaya.ini");
    }

    internal void LoadConfig(string configFile)
    {
        try
        {
            if (!File.Exists(configFile))
            {
                Logger.Error("Your config file couldn't be found.");

                Console.ReadKey(true);
                Environment.Exit(0);
            }

            if (CoreUtilities.IsFileLocked(new FileInfo(configFile)))
            {
                Logger.Error("Your config file is already open somewhere.");

                Console.ReadKey(true);
                Environment.Exit(0);
            }

            _configItems = File.ReadLines(configFile).Where(IsConfigurationLine).Select(line =&gt; line.Split('=')).ToDictionary(line =&gt; line[0], line =&gt; line[1]);
        }
        catch (Exception exception)
        {
            Logger.Fatal(exception, "Error while loading config.");

            Console.ReadKey(true);
            Environment.Exit(0);
        }
    }

    private static bool IsConfigurationLine(string line)
    {
        return !line.StartsWith("#") &amp;&amp; line.Contains("=");
    }

    internal string GetConfigValueByKey(string key)
    {
        if (!_configItems.TryGetValue(key, out var value))
        {
            Logger.Warn("Missing configuration key `" + key + "`");
        }

        return value;
    }
}
General

This class can't be reused because the path to the ini file is hardcoded. Having a parameter string path and using it to load the configuration would be a better way.

LoadConfig()

I don't like the Console.ReadKey(bool) and I don't see why the method should be able to terminate the process. In addition passing 0 to the Exit() method indicates that the process completed successfully which seems wrong.

The line where you assign the key/values to the dictionary is too long and therefor hard to read.

IMO the caller of the code should do the logging and you should let the exceptions bubble up the line.

Checking if the file is locked will pretence that it is safe to read the file, but it could happen that just after the check the file is opened and locked.


GetConfigValueByKey()

It seems strange that a not found key only will be logged. A better way would be to just use the bool TryGetXxx style.


How I would do this:

I would add an overloaded public constructor which takes a string fileName parameter. I would then make the parameterless constructor internal and let it call the public constructor with the &quot;resources/config/pitaya.ini&quot; to simulate the former functionality.

I would validate the fileName parameter and would throw the expected exceptions.

I would just initialize the dictionary in the constructor and if an exception occurs I would let it bubble up.


Like so
public class ConfigHandler
{
    private readonly Dictionary&lt;string, string&gt; _configItems = new Dictionary&lt;string, string&gt;();

    internal ConfigHandler() 
        : this(&quot;resources/config/pitaya.ini&quot;)
    { }

    public ConfigHandler(string fileName)
    {
        if (fileName == null)
        {
            throw new ArgumentNullException(nameof(fileName));
        }

        if (string.IsNullOrWhiteSpace(fileName))
        {
            throw new ArgumentException($&quot;The parameter {nameof(fileName)} may not be empty or whitespace&quot;, nameof(fileName));
        }

        if (File.Exists(fileName) == false)
        {
            throw new FileNotFoundException(&quot;File could not be found.&quot;, fileName);
        }

        _configItems = File.ReadLines(fileName)
                           .Where(IsConfigurationLine)
                           .Select(line =&gt; line.Split('='))
                           .ToDictionary(line =&gt; line[0], line =&gt; line[1]);
    }
           

    private static bool IsConfigurationLine(string line)
    {
        return !line.StartsWith(&quot;#&quot;) &amp;&amp; line.Contains(&quot;=&quot;);
    }

    public bool TryGetValue(string key, out string value)
    {
        if (key == null)
        {
            throw new ArgumentNullException(nameof(key));
        }
        
        return _configItems.TryGetValue(key, out value);
    }
}