Reputation: 991
I have a configuration class store application configuration. Currently I am using a static class. Some of the configurations are related to one topic so I want to organize them into a nested class, so I can reference configurations like this:
AppConfig.Url
AppConfig.LogSettings.FileSize
I have two options, either use a static nested class,
public static class AppConfig
{
public static class LogSettings
{
public static int FileSize {get; set;}
}
}
or declare a class but add a static property:
public static class AppConfig
{
public class LogSettings
{
public int FileSize {get; set;}
}
public static LogSettings logSettings { get; private set; }
}
However, none of them can protect nested class member FileSize being modified by other classes, even I use private set
to protect the public static property.
Maybe I should not use nested class to implement this? Any suggestions?
Upvotes: 3
Views: 2027
Reputation: 1856
I voted for Eric's answer but wanted to put this here as 'something to consider'.
public class Tester
{
public Tester()
{
AppConfig.LogSettings.FileSize = 5; // compile error
Console.WriteLine(AppConfig.LogSettings.FileSize); // works
}
}
public static class AppConfig
{
// just an example of setting the value in the outer class
private static void SetFileSize(int size)
{
fileSize = size; // internal only setting works
}
private static int fileSize; // a member of AppConfig still
public static class LogSettings
{
public static int FileSize
{
get { return fileSize; } // internal classes can access private members of the outer class
}
}
}
When you access AppConfig externally you get the grouping you desire, however inside the AppConfig class the 'grouping' is simply about publicly exposing the getter, the actual member variable still belongs to AppConfig.
This works because the internal class can access private members of the outer class.
So if your grouping goal is mostly about the public interface - how you get the values - this approach is simpler, but if your goal is also have the grouping internally too...then obviously it doesn't deliver.
Upvotes: 2
Reputation: 660407
The other solutions given so far essentially require the property being set to be set once. I am very much in favour of immutable objects, but they are not always practical; is it possible to solve your problem and make the property mutable?
This is in fact a special case of the more general problem you pose: how can we mark certain members of an inner class as being accessible only to the outer class, but not to anything outside of that class?
It is by no means obvious how to do so, but this is surprisingly easy. The key to the solution is to remember that interfaces may be private implementation details. C# requires that a base class be at least as accessible as the class deriving from it, but C# does not require that an implemented interface be as accessible as the class implementing it!
using System;
public static class Outer
{
private interface IPrivates
{
string Name { set; }
}
public readonly static Inner TheInner = new Inner();
private readonly static IPrivates TheInnerPrivates = TheInner;
public class Inner : IPrivates
{
public string Name { get; private set; }
string IPrivates.Name { set { this.Name = value; } }
}
public static void DoIt()
{
TheInnerPrivates.Name = "abc";
}
}
public class Program
{
public static void Main()
{
Outer.DoIt();
Console.WriteLine(Outer.TheInner.Name);
}
}
Code inside of Outer
may access the private members of Inner
via the interface. Code outside of Outer
cannot see anything other than the public members of Inner
, because the interface they need to see the private members is itself private.
Upvotes: 12
Reputation: 101563
One option is to use a constructor of nested class to initialize values. For example:
public static class AppConfig {
static AppConfig() {
Log = new LogSettings(1);
}
public class LogSettings {
public LogSettings(int fileSize) {
FileSize = fileSize;
}
public int FileSize { get; private set; }
}
public static LogSettings Log { get; private set; }
}
Then other classes can create instance of LogSettings
still, but cannot modify your instance.
Upvotes: 1
Reputation: 15129
If this is the approach you want to go with, I see 2 options:
You extract all the logic surrounding loading and parsing of the config file into a separate project (library), than you can mark your setters internal
and other libraries won't be able to access them anymore.
You can implement a more explicit property which doesn't allow to set a value twice like:
private int? _fileSize;
public int FileSize {
get { return _fileSize ?? 0; }
set {
if (_fileSize.HasValue) {
throw new InvalidOperationException("You can only set the value once");
}
_fileSize = value;
}
}
Another option, which I've often used, is to encapsulate the parsing of different parts of a configuration to the sub classes. You provide that information in the constructor and the class initializes itself. (just like Evk commented in the meanwhile)
Upvotes: 0