views:

201

answers:

3

I have the following situation. There is some very common class in my application that contains a static readonly field called "BinDirectory" that holds the path to the bin directory. Other fields in this class, which are static readonly too, use this value to as a base to their value. On the current version BinDirectory is initialized to hold the directory where the code is running (i.e. Assembly.GetExecutingAssembly().CodeBase). I want to extend this class to initialize BinDirectory to hold the "TargetDir" from the installer context when it is run from my application installer. I can change BinDirectory to be simply static but I don't want to, since it will make me do lots of changes to a class that is common in my app. Can somebody suggest an elegant solution to this problem?

+5  A: 

Make it a property with just the "get" accessor:

public static string BinDirectory
{
    get { return _initialisedBinDirectory; }
}

Then in your static constructor code, initialise the private variable as you need.

EDIT

Delayed load (as per comment):

public static string BinDirectory
{
    get
    {
        if (_initialisedBinDirectory == null)
            // load the variable when needed
        else
            return _initialisedBinDirectory;
    }
}

This way you only load the variable when you need it, and it's re-used whenever you call it again. Hopefully you don't class null as a valid value for it though.

Codesleuth
+1, that was exactly the solution I was typing :)
Zyphrax
This will not work since the static constructor will run when someone will first use my class which may happen before I call the BinDirectory propery.
Ikaso
I thought about a code generation solution or maybe another elegant way.
Ikaso
I don't understand your comment about the problem with this approach.
Sam Holder
A static constructor runs when your class is used for the first time. This means the When you write Class.BinDirectory the static constructor runs first and then the property is run. The code above assumes the _initialisedBinDirectory is initialized before the static constructor is called which is not actually feasible.
Ikaso
you can't write Class.BinDirectory as it is readonly (no set). the above code assumes you initialize _initializedBinDirectory in the static constructor.
Sam Holder
An alternative would be to simple have a 'delayed load' variable. See edit (pending).
Codesleuth
A: 

It sounds like you're loath to change a static readonly field to simply static because it would force you to change the initialization of all of the other static readonly fields in your class.

If that is correct, unfortunately there isn't a whole lot you can do but take the time to make the change. By allowing the BinDirectory field to be set at runtime you are fundamentally changing the initialization sequence of the fields. Your code will need to adapt.

I think the easiest way is to convert to using static readonly properties which do the calculation of the value on the fly.

For example:

public class Values { 
  public static string BinDir;
  public static string OtherDir { 
    get { return Path.Combine(BinDir,@"Some\Other\Path"); } 
  }
}
JaredPar
You might want to "cache" the result of the Path.Combine in a field. Change the BinDir to a property as well, and give it a private set operation.
Zyphrax
+3  A: 

This is what AppConfigs are for. In your AppSettings section, add a new key called BinDirectory. You can re-write your class as:

public static string BinDirectory
{
    get
    {
        return ConfigurationManager.AppSettings["BinDirectory"];
    }
}

Finally, as one of the last steps in your installation process, you can change the BinDirectory to point to any directory you want. So now this value is determined entirely by the installer context.

Juliet
I think you missed some parenthesis `()` or a `get` block in there
Codesleuth
But can I change AppSetting at runtime? If yes then this is the solution for me.
Ikaso
best answer here and yes you can change appsettings at runtime.
JonH
Thanks Juliet this is my solution
Ikaso
Some might suggest using a backing field, but it looks like `ConfigurationManager` only ever reads once and keeps its NameValueCollection in a static property.
Chris S