Tari has a neat post over at his blog about treating configuration as code with Python’s import hooks. I thought the way he handled it was really neat--basically, he loads in an INI file, and it creates a dynamic object with properties equal to the INI file. So, his example was a file such as:

Code:
[cat]
sound=meow
[dog]
sound=woof
[cow]
sound=moo

And it can be accessed like:

Code:
>>> import INIImport
>>> INIImport.init()
>>> from config import foo
>>> foo.cat
{'sound': 'meow'}
>>> foo.dog['sound']
'woof'

Well, I thought this was really cool and wanted to do something similar in C#. I found a neat class called ExpandoObject that would allow for this sort of thing to C#. So, for example, given the above INI file you could do:

Code:
dynamic foo = Config.ReadIni("Config.ini");
Console.WriteLine(foo.cat.sound);

And it would output "meow". Because of the way the objects work, it's also trivial to iterate over the properties and such. Here's a screen shot showing more advanced output with a slightly larger INI file:

The code to do that is very simple:

Code:
      static void Main(string[] args) {
         // Read in the INI file as a dynamic type.
         dynamic config = Config.ReadIni("Config.ini");

         // Access the properties directly as if it were an object.
         Console.WriteLine("Accessing properties directly:");
         Console.WriteLine("Cats say {0}.", config.cat.sound);
         Console.WriteLine("Dogs say {0}.", config.dog.sound);
         Console.WriteLine("Cows say {0}.", config.cow.sound);
         
         Console.WriteLine();
         
         // Because we're using ExpandoObjects, which implement IDictionary<string, object>,
         // we can also iterate over the collection. Each item in the config is another ExpandoObject
         // meaning we can iterate over that as well.
         Console.WriteLine("Iterating over properties:");
         foreach (var prop in ((IDictionary<string, object>)config)) {
            Console.WriteLine("A {0}:", prop.Key);
            foreach (var key in ((IDictionary<string, object>)prop.Value)) {
               Console.WriteLine("\t{0}={1}", key.Key, key.Value);
            }
         }

         Console.ReadKey();
      }

You can download the dll as well as the source and test program at:
http://merthsoft.com/DynamicConfig.zip (requires .net 4.0)

The INI loading is very primitive right now. It handles quoted values, but not escape characters. Start a line with ";" or "#" for comments.

Feel free to do whatever you want with it--I plan on adding it to TokenIDE for some better configuration of fonts and such, unless I change my mind and use XML.
ExpandoObject is a silly name, but a useful thing, apparently.

Neat to see that my experiment inspired something, but it seems like your solution misses some of the things that make the Python version particularly nice, such as ease of reloading: in the Python version, a simple reload(foo) (or impl.reload(foo) on Python 3.x) will reload the config and update the name binding in all active scopes, which is quite handy.
I noticed that in your blog post, though I didn't quite understand the impetus behind it--how often is one reloading a config file programmatically like that? I can see it's use a a REPL and such, but I'm not 100% sure about workflows for it. That being said, I can try adding that and see how it goes.

Quote:
ExpandoObject is a silly name, but a useful thing, apparently.
Heh, that's exactly what I thought! I can't help but wonder how that name came about; I think it has something to do with someone not being able to pronounce "expandable" when they were describing the functionality to the person who made it.
The config admittedly doesn't get reloaded often. But for long-running programs, it becomes very easy to reload the config from disk on-demand. Could even set something (very simple) up to watch the backing file and do the reload on modification (though that seems like a fragile way to do it).
Ooh, that definitely makes sense--I wasn't even thinking about long-running programs. C# has some file-monitoring stuff, too, so it would be pretty simple for a user to set something up like that if they wanted.
OK, I think I managed to accomplish this. Here's a screenshot:

And here's the code to do that:

Code:
Console.WriteLine("Reading in config 2.");
Config.ReloadIni(config, "Config2.ini");
foreach (var prop in ((IDictionary<string, object>)config)) {
   Console.WriteLine("A {0}:", prop.Key);
   foreach (var key in ((IDictionary<string, object>)prop.Value)) {
      Console.WriteLine("\t{0}={1}", key.Key, key.Value);
   }
}

So you can see the real work is just Config.ReloadIni(config, "Config2.ini"); which is pretty simple to do. Download in the same place:
http://merthsoft.com/DynamicConfig.zip
Code's there if anyone wants to see how I managed it.
I added the ability to save a file from a config, the code is super easy, just do:

Code:
Config.WriteIni(config, "Config3.ini");

or pass an existing streamwriter.
Download in the same place:
http://merthsoft.com/DynamicConfig.zip
Code's there, I know Jonimus was wanting it.
I like this much better than the xml stuff you helped me hack up a while back, though that may be saner for my users.

Either way thanks, it looks awesome Merth!
I think for some things XML makes more sense, but for config INI is a little easier to work with, at least in my opinion.

I hope this suits your needs well; be sure to tell me both of errors and cool usage Smile
There is now a github repo:
https://github.com/merthsoft/DynamicConfig
Moved to bitbucket:
https://bitbucket.org/merthsoft/dynamicconfig
Two and a half year update! I've made some changes to it so that now, with some configuration passed in when reading, you can make it so it returns null if you try to access a member that does exist, instead of throwing an exception. I also made it so that you can have the members be case-insensitive.
https://bitbucket.org/merthsoft/dynamicconfig
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement