I created a C# code for logging error codes.
I hardcoded the error codes into a class RecordId
as static int
s.
public class RecordId
{
public static int UnknownCommand = 100;
public static int SoftwareVersion = 101;
public static int WarningError = 110;
public static int AbortError = 111;
// etc...
}
Having static int means that I can do RecordId.SoftwareVersion
anywhere in my code, I don't actually need to instantiate the class RecordId, which is very convenient, since I want to be able to log things from different parts of the code by calling a Log class that also doesn't need instantiation (it just appends a message to a file)
The logging function is also static, being something like
public class Logger
{
public static void LogExperiment(int key, string value)
{
// Append key and value to a hardcoded filename
}
}
Then from anywhere in my code I can do
Logger.LogExperiment(RecordId.SoftwareVersion, "1.0");
This will just append 101 1.0
in a log file
I don't need instances of the classes, so I can log anywhere from my code.
Now, as the code grows, I don't want to modify the code every time I add a new RecordId
, so I want to have a JSON file where I load the values into the class.
I modified the RecordId class to look like:
public class RecordIdNew
{
public String UnknownCommand { get; set; }
public String SoftwareVersion { get; set; }
public String WarningError { get; set; }
public String AbortError { get; set; }
}
The problem I see now, is that in order to populate this values from the JSON file I have to instantiate the class RecordId, whereas before I was using the values as static ints, and therefore I could call RecordId.SoftwareVersion
The question (which might be a bit open) is: Is there a way I can keep RecordId not instantiated, but access values that come from a JSON file.
Or if not possible, is there another structure that would allow me to do that?
CodePudding user response:
You are looking for static constructor, i.e.
// Let's have class being static if you don't want to create instances
public static class RecordId
{
// To be on the safer side of the road, let's have readonly fields:
// once set in the static constructor they can't be changed
public static readonly int UnknownCommand;
public static readonly int SoftwareVersion;
public static readonly int WarningError;
public static readonly int AbortError;
// Static constructor, it will be called before the first read of any field
static RecordId() {
//TODO: put your logic here: read the file and assign values to the fields
}
}
Edit:
Please, have a look at the your current design, maybe you are looking for {Key, Value}
pairs? E.g. Key == 100, Value == "UnknownCommand"
etc.
If it's your case, try using Dictionary
:
public static class RecordId {
private static readonly Dictionary<int, string> s_Names = new();
public IReadOnlyDictionary<int, string> Names => s_Names;
static RecordId() {
//TODO: Your logic here (fill in s_Names)
}
}
usage:
int code = 100;
if (RecordId.Names.TryGetValue(code, out var name))
Console.WriteLine($"{code} is {name}");
else
Console.WriteLine("Unknown code");
CodePudding user response:
Assuming you can perfectly match up the static C# properties or fields to the values in the JSON, you can use ModuleInitializerAttribute to set the static properties.
public static class RecordId
{
public static int UnknownCommand { get; private set; }
public static int SoftwareVersion { get; private set; }
public static int WarningError { get; private set; }
public static int AbortError { get; private set; }
// etc...
[ModuleInitializer]
public static void Init()
{
// code to read JSON
// loop over JSON fields, matching them to
// above fields, setting their values...
}
}
This gives you a way to set the values at runtime, once, when the module loads (modules are groups of logical code in an assembly (reference)).
Module initializers are guaranteed to be run before any other access to the module; so if you reference, say, UnknownCommand
anywhere, you will get the value that was read from the JSON. In fact, as Dmitry notes in the comments, the module init code is guaranteed to run period, even if no other code in the module is accessed at all. This could be a drawback if the code is slow or buggy, but useful in cases such as yours.
This does not give you a way to dynamically create the properties; that would require either code generation prior to compilation or access to the values at runtime via some sort of "Get" method coupled with a static dictionary.
Here's an article on the subject, and here's the original proposal on GitHub.