views:

33

answers:

4

Hey everyone,

I have a class filled with properties. When the properties are accessed it reads some values out of an XDocument.

public class Foo
{
 private XDocument root;

 public Foo(Stream str)
 {
  root = XDocument.load(str);
 }

 public String Bar
 {
  get
  {
   return root.Element("bar").Value;
  }
 }

}

Only it seems a bit of overhead, since everytime it get accessed it has to read the XDocument again. I tried to 'cache' this a little bit as following

public String Bar
{
 get
 {
  if(String.IsNullOrEmpty(this.Bar))
   return root.Element("bar").Value;
  else
   return this.Bar;
 }
}

This seems pretty decent for me, only I have one problem with it. The class has like ~200 properties. Everytime I have to do this check, and since OOP is all about not copying large parts of code is there any way to make this work like this automatically?

+2  A: 

Keep the properties within an enum and cache the values in a hashtable. Provide a single property accessor.

not tested:

enum MyProperties {
    Prop1,
    Prop2
}

// ...

static class PropertyProvider {
    static Hashtable<MyProperties, Object> cache = new Hashtable<MyProperties, Object>();
    static Object getProperty(MyProperties prop) {
        if (!cache.ContainsKey(prop)) {
            cache.add(prop, "SOMETHING");
        }

        return cache[prop];
    }
}

// ...

Object result = PropertyProvider.getProperty(MyProperties.Prop1);
thelost
+2  A: 

As "thelost" mentioned, don't have a single field for each property. Keep a dictionary as a cache, so you don't pay anything for properties you haven't accessed.

I would suggest you then have a method which you can provide with the mechanism for retrieving the real value. For example:

public class Foo
{
    private enum CacheKey
    {
        Bar, Baz, ...;
    }

    private readonly XDocument doc;
    private readonly Dictionary<CacheKey, string> cache;

    private string Fetch(CacheKey key, Func<XDocument, string> computation)
    {
        string result;
        if (!cache.TryGetValue(key, out result))
        {
            result = computation(doc);
            cache[key] = result;
        }
        return result;
    }

    public string Bar
    {
        get { return Fetch(CacheKey.Bar, doc => doc.Element("bar").Value); }
    }
}

That way each property ends up reasonably compact - it basically expresses the cache key involved, and how to compute the property. If you need properties of different types, you might want the cache to just have TValue as object, and make the Fetch method generic, casting where necessary. That will end up boxing value types, admittedly.

If you use this approach in several places, you may well want to create a generic ComputingCache class to avoid repeating the logic.

Jon Skeet
+1  A: 

is the lazy part important? if not, just populate all the properties at ctor time (they can all be auto) and you're done. I'd rather do this since it fails faster if there's a problem with the xml. If lazy is important, then in .NET 4 you can use Lazy for them.

Note that whatever caching approach you go with, make sure 1) you don't have stack overflow problems in your getter's and 2) empty/missing values are still considered valid for populating a cache entry (if that's indeed true for the source xml)

James Manning
+1  A: 

If you are using .NET 4.0, you could use the handy Lazy class.

http://weblogs.asp.net/gunnarpeipman/archive/2009/05/19/net-framework-4-0-using-system-lazy-lt-t-gt.aspx

If you're using a previous version of the framework, you could just code your own version of the Lazy class (it's not particularly complicated).

RQDQ
You'll still end up with one field per property, which it sounds like the OP doesn't want.
Jon Skeet