11

I want to load a properties file (it's a .csv file having on each line a name and associated numeric value) and then access those property values like so: FileLoader.PropertyOne or FileLoader.PropertyTwo. The problem is I don't want to have to write a property for each value, I want them to be generated from the file. So

public class FileLoader
{
    public int Property1 { get; private set; }
}

is not what I'm looking for. Is this possible? I can't see any way to do it because obviously the compiler wouldn't know about the property names. Perhaps something similar?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Johnny
  • 7,073
  • 9
  • 46
  • 72

7 Answers7

15

In C# 4.0, you could use the ExpandoObject, link contains good explanation and a couple of use cases, like :

dynamic contact = new ExpandoObject();
contact.Name = "Patrick Hines";
contact.Phone = "206-555-0144";
contact.Address = new ExpandoObject();
contact.Address.Street = "123 Main St";
contact.Address.City = "Mercer Island";
contact.Address.State = "WA";
contact.Address.Postal = "68402";

Though the awesomeness of the ExpandoObject is to dynamically create complex hierarchical objects, I suppose you could use it in general for it's shiny syntax even for simple dynamically defined objects.

EDIT: Here is another answer on SO adding details about the benefits of ExpandoObject by the same columnist that wrote the previously linked article

What are the true benefits of ExpandoObject?

jpvantuyl
  • 584
  • 10
  • 22
Dynami Le Savard
  • 4,986
  • 2
  • 26
  • 22
  • 5
    In my opinion, this is one of the cases that illustrate why dynamic is a bad idea. No Intellisense, no type safety, no compile-time checks, runtime exceptions - I think code generation would be a whole lot better. – OregonGhost Feb 04 '10 at 16:32
  • Even before ExpandoObject, one could achieve similar results with a `Dictionary` object in vb6, though `Dictionary` could only hold things of type `Object`: `MyDict!Address!Street = MyStreetObject`. One could achieve something similar in vb.net, though one would have to have a named property of the last object [e.g. `st` for `string`]: `MyDict!Address!City.st = "Mercer Island"`; such an approach probably had about the same pros and cons as ExpandoObject. – supercat Oct 23 '12 at 22:19
  • How to add methods? – Sathiyamoorthy Feb 26 '19 at 08:49
2

Code generation like this can be done in several different ways.

  • Visual Studio has T4 templates.
  • You can use external tools like My Generation (it is geared towards ORM code generations, not sure it supports CSV).
  • Or roll out your own code to read the file and write out such classes (to be created with a .generated.cs suffix).
Oded
  • 489,969
  • 99
  • 883
  • 1,009
2

Without having your "FileLoader" actually rewrite itself, and then using Reflection to access the newly created properties ther's not really any way to do this. (Completley ignore this answer if you're after something that works at Design/Compile time, rather than at runtime =)

What you'll probably end up doing is having something like

public class FileLoader
{
  // ... Other code for FileLoader goes here

  public FileLoader(string propertiesFileNameAndPath)
  {
    // Load values from propertiesFile into _properties / _propertyValues
  }

  private _properties = new List<string>();
  private _propertyValues = new Dictionary<string, string>();

  public string[] Properties
  {
    // returning as an array stops your _properties being modified
    return _properties.ToArray();
  }
  public void UpdateProperty(string propertyName, string propertyValue)
  {
    if (propertyValues.ContainsKey(propertyName))
    {
      propertyValues[propertyName] = propertyValue;
    }
  }
  public string GetPropertyValue(string propertyValue)
  {
    if (propertyValues.ContainsKey(propertyName))
    {
      return propertyValues[propertyName];
    }
  }
}

Now you can do:

var fileLoader = new FileLoader("path to properties.csv");
foreach(var property in fileLoader.Properties)
{
  var propertyValue = fileLoader.GetPropertyValue(property);
}

Of course you could just simplify it down to loading it into a dictionary that you return from a method on FileLoader, but the code above maintains part of the "appearance" of using properties of the FileLoader class =)

Edit: Add "indexer" code

One thing that might make the syntax cleaner would be to use an "Indexer", so you'd add the following to FileLoader:

  public string this[string index]  
  {
    if (propertyValues.ContainsKey(propertyName))
    {
      return propertyValues[propertyName];
    }
    else
    {
      return null;
    }
  }

Then the code for accessint it would be the slightly tidier:

var fileLoader = new FileLoader("path to properties.csv");
foreach(var property in fileLoader.Properties)
{
  var propertyValue = fileLoader[property];
}
Rob
  • 45,296
  • 24
  • 122
  • 150
  • 1
    +1 for indexer. That's what I thought straight away when reading the question. – Wim Feb 04 '10 at 16:37
2

Here's a sample using ExpandoObject and C#`s 4.0 dynamic feature

public dynamic ParseCsvFile(string filePath) {
  var expando = new ExpandoObject;
  IDictionary<string,object> map = expando;
  foreach ( var line in File.ReadAllLines(filePath)) {
    var array = line.Split(new char[]{','},2);
    map.Add(array[0],array[1]);
  }
  return expando;
}

...
var d = ParseCsvFile(someFilePath);
Console.WriteLine(d.Property1);
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
1

You'll basically need to do some code generation.

Write a simple console app, or win forms app, that loads the csv, then takes the information from the csv and generates .cs files.

Jack Marchetti
  • 15,536
  • 14
  • 81
  • 117
  • I think he wants them to be generated at run-time, rather than compile time, although generating code through a macro (or script or whatever) is still a good route. Run-time generation might be necessary if the properties are changing while the app is running. – FrustratedWithFormsDesigner Feb 04 '10 at 16:24
  • 1
    I think it's about compile-time, because otherwise it wouldn't really make sense to have real properties and *not having to write the declarations by hand*. However. – OregonGhost Feb 04 '10 at 16:34
1

There's an easy solution for that, read the properties into a dictionary and overload the FileLoader's array operator:

public T this[string propertyName]  
{  
    get { return yourDictionary[propertyName]; }  
}

That way, you can access the properties using fileLoadObject["SomePropertyName"].

As Oded pointed out, it is possible to dynamically add properties with Reflection. There's an example over here.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
AndiDog
  • 68,631
  • 21
  • 159
  • 205
  • 1
    What about Reflection? You can create dynamic objects with it. – Oded Feb 04 '10 at 16:25
  • @AndiDog - you can use reflection to create properties in C#. I have some example code somewhere that I've used it on, just have to dig it up – Cody C Feb 04 '10 at 16:29
  • Ok, I edited the answer to provide the correct infos. I also added a tutorial for creating properties dynamically. – AndiDog Feb 04 '10 at 16:42
0

This is possible with the new dynamic stuff in c# 4.0. I'm not an expert, but with dynamic objects you can define behavior for when a method that does not exist is evoked. This post shows a pretty fun example of how to set up a dynamic dictionary that could do what you want.

captncraig
  • 22,118
  • 17
  • 108
  • 151