1

I have a class named Configurationwhich inherits from DynamicObject. It it a Dictionary<string, object>. In the constructor, it reads a text file and loads all the values from it splitted by =, so you can create dynamic configuration objects like this:

dynamic configuration = new Configuration("some_file.ini");

Here is my full class:

public sealed class Configuration : DynamicObject
{
    private string Path { get; set; }
    private Dictionary<string, object> Dictionary { get; set; }

    public Configuration(string fileName)
    {
        this.Path = fileName;
        this.Dictionary = new Dictionary<string, object>();

        this.Populate();
    }

    ~Configuration()
    {
        if (this.Dictionary != null)
        {
            this.Dictionary.Clear();
        }
    }

    private void Populate()
    {
        using (StreamReader reader = new StreamReader(this.Path))
        {
            string line;
            string currentSection = string.Empty;

            while ((line = reader.ReadLine()) != null)
            {
                if (string.IsNullOrWhiteSpace(line))
                {
                    continue;
                }

                if (line.StartsWith("[") && line.EndsWith("]"))
                {
                    currentSection = line.Trim('[', ']');
                }
                else if (line.Contains("="))
                {
                    this.Dictionary.Add(string.Format("{0}{1}{2}",
                        currentSection,
                        (currentSection != string.Empty) ? "_" : string.Empty,
                        line.Split('=')[0].Trim()),
                        ParseValue(line.Split('=')[1].Trim().Split(';')[0]));
                }
            }
        }
    }

    private object ParseValue(string value)
    {
        if (string.IsNullOrWhiteSpace(value))
            return string.Empty;

        if (value.StartsWith("\"") && value.EndsWith("\""))
            return value.Substring(1, value.Length - 1);

        if (IsNumeric(value))
            return Convert.ToInt32(value);

        // TODO: FIXME Floating values (not to be confuse with IPAddress).
        //if (IsFloating(value))
        //    return Convert.ToDouble(value);

        if (string.Compare(value, "true", StringComparison.OrdinalIgnoreCase) == 0)
            return true;

        if (string.Compare(value, "false", StringComparison.OrdinalIgnoreCase) == 0)
            return false;

        return value;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (this.Dictionary.ContainsKey(binder.Name))
        {
            if (this.Dictionary[binder.Name] == null)
            {
                result = null;
            }
            else
            {
                result = this.Dictionary[binder.Name];
            }

            return true;
        }

        throw new ConfigurationException(binder.Name);
    }

    public override string ToString()
    {
        string result = this.Path + " [ ";

        int processed = 0;

        foreach (KeyValuePair<string, object> value in this.Dictionary)
        {
            result += value.Key;
            processed++;

            if (processed < this.Dictionary.Count)
            {
                result += ", ";
            }
        }

        result += " ]";

        return result;
    }

    private static bool IsNumeric(string value)
    {
        foreach (char c in value)
            if (!char.IsDigit(c))
                return false;

        return true;
    }
    private static bool IsFloating(string value)
    {
        foreach (char c in value)
            if (!char.IsDigit(c) && c != '.')
                return false;

        return true;
    }

    private class NullObject { }
}

Everything is working flawlessly. I've overriden TryGetMember and I'm returning the object based on the GetMemberBinder Name property. However, I'm facing a problem.

When I execute the following code:

string s = "some_config_key";

string d = configuration.s;

It retrieves the value of the key s rather than some_config_key, meaning that it doesn't evalute the value of the variable s. Is there a workaround for this?

Thanks.

4
  • @user2864740 Edited. Sorry. Commented Aug 18, 2014 at 7:06
  • See stackoverflow.com/questions/3309191/… Commented Aug 18, 2014 at 7:12
  • 1
    Why don't you also implement the this[string] property, then you could write configuration[s] Commented Aug 18, 2014 at 7:13
  • I would use indexers too, like @Dirk suggested Commented Aug 18, 2014 at 7:25

1 Answer 1

2

C# dynamic features are partially compiled like the property/method access. To understand this lets take an example.

dynamic myVar = new { a=1, b="test" };
myVar.a += 1;

Here the C# type system would not test for the validity of the property while compilation, only at runtime the code is executed and if the property is not found it will through you runtime error.

So in your code the property access is already complied to access the property named "s" on the dynamic object "configuration".

In C# no-way you can do this not even in dynamically typed languages like python or javascript.

You have to use like this.

dynamic configuration = getConfig("path/to/file");
var myobj = configuration as IDictionary<string,object>;
string s = "config_key";
var value = myobj[s]; // this is correct.
Sign up to request clarification or add additional context in comments.

1 Comment

Just a minor point: properties of anonymous objects are read-only, so myVar.a += 1 will always throw an exception in your case, even if myVar.a exists.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.