What's the best way to implement a global constant in C#?

24,090

Solution 1

It depends. If it is truly a constant that won't change, even in future versions of your code, then const is fine. Else go with a static readonly field.

A const will get embedded into the calling assembly, whereas with static readonly the calling assembly only contains a reference to the field. This means const requires recompilation of all dependent code whenever you change the value, whereas public readonly uses the new value even without recompiling the calling assembly.

If you want to store the "constant" in a config file, but like Intellisense, you can use a property with no public setter. And then fill it from the config file at runtime. But I'd argue that configuration values should not be static in the first place. For configuration values I'd use a singleton of some sort, preferably the IoC variation and not the Class.Instance variation. So I'd just define an interface like the following:

interface IMyConfig
{
  string Key{get;}
}

And have classes that need this config take it as a constructor parameter:

public MyClass(IMyConfig config)
{
    ...
}

Solution 2

If you think you'd be changing it and you're worried about having to compile it, then why not use appSettings in the web config file? That's what it's for. If you really need intellisense then you could just put a class in one of the assemblies that reads the config value and exposes it as a property for easier referencing. If it's sensitive data then I wouldn't put it in a config file, I would just compile it anyways since you don't want to compromise your application.

<appSettings>
    <add key="myconstant" value="here's the value!" />
</appSettings>

Here's the class to reference that value, which gives you intellisense, ability to change it easily in the future, and without having to recompile anything

public class MyAppConfigSettings
{
    public string MyConstant { get; private set; }

    public MyAppConfigSettings()
    {
        MyConstant = ConfigurationManager.AppSettings["myconst"];
    }
}

It may not be the answer to your solution but it may give you some other ideas.

Solution 3

If you are activating fxCop (code analysis tool included in Visual studio distribution), you may get sugestion to change constant to become:

public static readonly string ConstName = "a value";

Solution 4

I'm not sure if I understand the problem completely... you're asking for a solution to storing some global variables that won't cause recompiles to assemblies that reference those global variables if you change them? If so then why not try thinking about redesigning your architecture as per the Inversion of Control principle? Think "don't call us, we'll call you" the hollywood principle. If all the assemblies that require some const just call an interface (that they own) that exposes a property with the value they require, and then you have a project of constants that implement those interface (by referencing those projects and then implementing those interfaces) then those projects will never need recompilling when you change the value of the constants.

I'm sure you know them anyway but have a read up on the SOLID principles, "D" being the Dependency Inversion principle (Inversion of Control). I think given your concerns (assuming I've understood you right) they could really help you out.

An example of Inversion of Control could be as simple as:

MyService.dll :

public class MyService
{

    // injected dependency
    public IMyConstants MyConstants { get; set; }

    public MyMethod(){

        // get your query...
        var query = IMyConstants.Query;
    }

}

MyConstants.dll :

public MyConstants : IMyConstants {

    // implementation of query property from the myservices.dll interface
    public string Query { ... }

}

So the myconstants.dll references the myservice.dll rather than the other way around (meaning myservices won't need recompiling). Then the bootstrapping code (to set it all up and inject dependencies) lives elsewhere.

Sorry if I misunderstood you, hope that helps though!

Share:
24,090
The Light
Author by

The Light

I love thinking and writing .NET applications and have over 13 years experience + MCPD, MCTS, MCAD, MCP.

Updated on December 15, 2020

Comments

  • The Light
    The Light over 3 years

    I have a Common project inside which I've added my public constants for QueryStringNames.

    I know generally constants should be as internal or private but I'd need public constants here as I'd like to allow a global access to the query string names, session keys, etc.

    There are 3 solutions that I know of but all of them have an important issue. The caller assembly would contain the copy of my constant which means if I have to change a constant value, I'll have to compile both my Common assembly and the caller assembly!

    1) public const string ConstName = "a value";
    2) public readonly string ConstName = "a value";
    3) To be stored in a public resource file.
    

    What would be the best approach to define public constants in C# apart from storing them in the web.config file (which doesn't have intellisense)?

  • spender
    spender over 12 years
    I don't have a compiler to hand,but I'm surprised that the static keyword would be required or even legal, given the nature of a const value.
  • The Light
    The Light over 12 years
    why?! I tested and static readonly would also need the recomilation of the caller.
  • CodesInChaos
    CodesInChaos over 12 years
    I'm pretty sure that changing a static readonly field doesn't require recompiling the caller. The calling assembly only contains the name of your field, and not the value of it.
  • spender
    spender over 12 years
  • Smith.Patel
    Smith.Patel over 12 years
    @CodeInChaos is correct. If you were to use a constant, that value is inserted by the compiler, whereas the readonly field is inserted at runtime (either at declaration or in the constructor, depending on how you implement the readonly value). Even a static readonly would be done at runtime, whenever the static fields for the class are initialized.
  • danludwig
    danludwig over 12 years
    I agree, if there is a possibility they can change (and cause recompile), then they're not really constants. I prefer const as they are easier to use in switch / case statements. But if you are talking about querystring & session keys, changing those will cause a recompile and redeploy anyway.
  • CodesInChaos
    CodesInChaos over 12 years
    I'd rewrite the constructor to take a NameValueCollection or something similar instead of having it fetch from the static AppSettings property. And then inject it via IoC.
  • The Light
    The Light over 12 years
    yeah, I hoped there would be a simpler solution without using the web.config. I'd usually write my own ConfigWrapper and IConfig and a singleton class as my CustomConfigManager to have all these properties as read-only and to interact with the physical file only once. if a change is required, then the app should be restarted. So is the web.config the Only way to avoid this issue?
  • CodesInChaos
    CodesInChaos over 12 years
    You're right, configuration should be injected via IoC and not read from static members. A few nitpicks: I'd use constructor injection, and I wouldn't use the name Constant here, since a configuration value isn't really a constant. If it were truly a constant we wouldn't need DI here.
  • The Light
    The Light over 12 years
    For the problem I pointed out, they'd both behave similarly. Callers Recomilation would be required in the case of a change unless the values are initialized from a separate physical file such as a config file.
  • The Light
    The Light over 12 years
    @CodeInChaos, it does apparently; test it yourself and see! or maybe I haven't tested properly.
  • lnu
    lnu over 12 years
    in your case I'd go with const in a static helper class.
  • CodesInChaos
    CodesInChaos over 12 years
    Wikipedia quotes the I in SOLID as "Interface segregation principle". I think the principle you want is "Push, don't pull".
  • CodesInChaos
    CodesInChaos over 12 years
    @William Perhaps you're confused by VS rebuilding all dependent assemblies by default. But if you take the new "static readonly" containing assembly and simply replace that single dll with a different version, all assemblies building on it should still work, and use the new values.
  • Jeff LaFay
    Jeff LaFay over 12 years
    I'm not sure if it's the only way to avoid it. I mean you could persist it in a database record as well. That way you could change the value when needed and not have to recompile. Honestly though, this really isn't that complex of a solution; it's pretty simple IMO.
  • Jeff LaFay
    Jeff LaFay over 12 years
    @CodeInChaos, that's totally acceptable too but my implementation encapsulates reading from the config completely and prevents client code that uses the class to have to fetch a NameValueCollection to pass it. Keep's it loosely coupled. If you need to write unit tests against the config class (I don't know why you would, there isn't any logic to test) you could provide a second constructor that takes a NameValueCollection. I think that would be over doing it though.
  • CodesInChaos
    CodesInChaos over 12 years
    @spender are you referring to an earlier version of this answer? Because static readonly certainly makes sense. static is only implicit when using const and not when using readonly.
  • james lewis
    james lewis over 12 years
    yeah that sounds about right - you know what I mean though. I'm just getting mixed up with some interpretations of DI I think!
  • CodesInChaos
    CodesInChaos over 12 years
    Your class is tightly coupled to the configuration storage mechanism. But client code shouldn't construct instances of MyAppConfigSettings. The only code constructing MyAppConfigSettings should be the IoC container. I'm not talking about testing the config class, but about testing classes that depend on the config class. And testing with a different configuration sounds pretty standard to me.
  • Jeff LaFay
    Jeff LaFay over 12 years
    So if you needed to test classes that use the config, couldn't you just write an interface for the config class and then mock it? I'm not a unit test guru but that's how I would do it.
  • spender
    spender over 12 years
    @CodeInChaos : It's possible that I misread the answer. At time of posting my comment, I thought it contained both const and static in the same declaration.
  • CodesInChaos
    CodesInChaos over 12 years
    @jlafay yes, if you have an interface and the client code only refers to the interface, and never to the class(especially not constructing it), your class is fine.
  • The Light
    The Light over 12 years
    @CodeinChaos, I had manually copied the assemblies; otherwise VS would have rebuilt everything.
  • The Light
    The Light over 12 years
    you didn't understand the problem.
  • james lewis
    james lewis over 12 years
    To anyone else who ever reads my above comment - I've edited a little to contain the correct reference to the SOLID principles. Cheers @CodeInChaos !