I'm currently storing a list of Tests with associated information, including database IDs, inside static variables in my code. Here's an example:
public static class IsokineticTests
{
// Cervical Spine Group
public static MyopathologyTest NeckFlexors = MyopathologyTest.Create(600, "Neck Flexors");
public static MyopathologyTest AnteriorObliques = MyopathologyTest.Create(601, "Anterior Obliques");
public static MyopathologyTest NeckExtensors = MyopathologyTest.Create(602, "Neck Extensors");
public static MyopathologyTest PosteriorObliques = MyopathologyTest.Create(603, "Posterior Obliques");
public static MyopathologyTest LateralFlexion = MyopathologyTest.Create(604, "Lateral Flexion");
// Shoulder Group
public static MyopathologyTest ShoulderAbductors = MyopathologyTest.Create(610, "Shoulder Abductors");
public static MyopathologyTest ShoulderExtensors = MyopathologyTest.Create(611, "Shoulder Extensors");
public static MyopathologyTest ShoulderFlexors = MyopathologyTest.Create(612, "Shoulder Flexors");
public static MyopathologyTest ShoulderLateralRotators = MyopathologyTest.Create(613, "Shoulder Lateral Rotators");
public static MyopathologyTest ShoulderMedialRotators = MyopathologyTest.Create(614, "Shoulder Medial Rotators");
}
These then get used to create a test group through other static properties get methods:
public static class IsokineticTestGroups
{
public static IsokineticTestGroup CervicalSpine
{
get
{
return IsokineticTestGroup.Create("Cervical Spine",
new List<MyopathologyTest>
{
IsokineticTests.NeckFlexors,
IsokineticTests.AnteriorObliques,
IsokineticTests.NeckExtensors,
IsokineticTests.PosteriorObliques,
IsokineticTests.LateralFlexion
});
}
}
}
The problem I'm having now is there is essentially no way to lookup the MyopathologyTest for a specific ID. One solution would be to create a dictionary and manually insert all these tests into the dictionary with the ID as the key. This seems like repeating a lot of code though.
What is the proper way to be implementing something like this? I'm thinking I need to externalize the test data, load in the tests during runtime and generate the proper look-up tables and groups on the fly. Is this the right approach I should be looking at or is there a better way?
Why not keep the test information in a database, like SQLite, and fetch the data on an as-needed basis using ADO.NET libraries? Response time is virtually instantaneous, so you have no need to store many test objects in a dictionary.
Related
I created a C# code for logging error codes.
I hardcoded the error codes into a class RecordId as static ints.
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?
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");
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.
How do I declare a variable so that every class (*.cs) can access its content, without an instance reference?
In C# you cannot define true global variables (in the sense that they don't belong to any class).
This being said, the simplest approach that I know to mimic this feature consists in using a static class, as follows:
public static class Globals
{
public const Int32 BUFFER_SIZE = 512; // Unmodifiable
public static String FILE_NAME = "Output.txt"; // Modifiable
public static readonly String CODE_PREFIX = "US-"; // Unmodifiable
}
You can then retrieve the defined values anywhere in your code (provided it's part of the same namespace):
String code = Globals.CODE_PREFIX + value.ToString();
In order to deal with different namespaces, you can either:
declare the Globals class without including it into a specific namespace (so that it will be placed in the global application namespace);
insert the proper using directive for retrieving the variables from another namespace.
You can have static members if you want:
public static class MyStaticValues
{
public static bool MyStaticBool {get;set;}
}
First examine if you really need a global variable instead using it blatantly without consideration to your software architecture.
Let's assuming it passes the test. Depending on usage, Globals can be hard to debug with race conditions and many other "bad things", it's best to approach them from an angle where you're prepared to handle such bad things. So,
Wrap all such Global variables into a single static class (for manageability).
Have Properties instead of fields(='variables'). This way you have some mechanisms to address any issues with concurrent writes to Globals in the future.
The basic outline for such a class would be:
public class Globals
{
private static bool _expired;
public static bool Expired
{
get
{
// Reads are usually simple
return _expired;
}
set
{
// You can add logic here for race conditions,
// or other measurements
_expired = value;
}
}
// Perhaps extend this to have Read-Modify-Write static methods
// for data integrity during concurrency? Situational.
}
Usage from other classes (within same namespace)
// Read
bool areWeAlive = Globals.Expired;
// Write
// past deadline
Globals.Expired = true;
A useful feature for this is using static
As others have said, you have to create a class for your globals:
public static class Globals {
public const float PI = 3.14;
}
But you can import it like this in order to no longer write the class name in front of its static properties:
using static Globals;
[...]
Console.WriteLine("Pi is " + PI);
How do I declare a variable so that every class (*.cs) can access its content, without an instance reference?
In C# you cannot define true global variables (in the sense that they don't belong to any class).
This being said, the simplest approach that I know to mimic this feature consists in using a static class, as follows:
public static class Globals
{
public const Int32 BUFFER_SIZE = 512; // Unmodifiable
public static String FILE_NAME = "Output.txt"; // Modifiable
public static readonly String CODE_PREFIX = "US-"; // Unmodifiable
}
You can then retrieve the defined values anywhere in your code (provided it's part of the same namespace):
String code = Globals.CODE_PREFIX + value.ToString();
In order to deal with different namespaces, you can either:
declare the Globals class without including it into a specific namespace (so that it will be placed in the global application namespace);
insert the proper using directive for retrieving the variables from another namespace.
You can have static members if you want:
public static class MyStaticValues
{
public static bool MyStaticBool {get;set;}
}
First examine if you really need a global variable instead using it blatantly without consideration to your software architecture.
Let's assuming it passes the test. Depending on usage, Globals can be hard to debug with race conditions and many other "bad things", it's best to approach them from an angle where you're prepared to handle such bad things. So,
Wrap all such Global variables into a single static class (for manageability).
Have Properties instead of fields(='variables'). This way you have some mechanisms to address any issues with concurrent writes to Globals in the future.
The basic outline for such a class would be:
public class Globals
{
private static bool _expired;
public static bool Expired
{
get
{
// Reads are usually simple
return _expired;
}
set
{
// You can add logic here for race conditions,
// or other measurements
_expired = value;
}
}
// Perhaps extend this to have Read-Modify-Write static methods
// for data integrity during concurrency? Situational.
}
Usage from other classes (within same namespace)
// Read
bool areWeAlive = Globals.Expired;
// Write
// past deadline
Globals.Expired = true;
A useful feature for this is using static
As others have said, you have to create a class for your globals:
public static class Globals {
public const float PI = 3.14;
}
But you can import it like this in order to no longer write the class name in front of its static properties:
using static Globals;
[...]
Console.WriteLine("Pi is " + PI);
I have static class that holds some info
public static class SampleDataCache
{
private static Dictionary<string,SampleData> cacheDict = new Dictionary<string,object>()
public static Get(string key)
{
if(!cacheDict.Contains[key])
cacheDict.Add(key,new SampleData());
return cacheDict[key];
}
}
And when I refresh page I want SampleDataCache to keep its data.
Can I achieve this in simple way?
Since the cache, in its current form, is stored in memory then the data is naturally cast into oblivion when the page refreshes - that's a new instance of the application starting there. You might, however, be interested in utilising isolated storage in order to persist data per-user.
With isolated storage you essentially have a contained file system into which you can store data and then further retrieve it. One step in the right direction could be to make a class you want to represent a 'piece' of cached data, make it serializable, then using your static class as the cache controller you can read and write these objects from and to isolated storage.
Quickstart: Isolated Storage in Silverlight
You should remember about extra if (nobody understand that ;/). And also you can be more generic and type safely. You can Look below, this is example of well written caching pattern also can be used as an aspect.
using System;
using System.Collections.Generic;
namespace SampleDataCache {
public class SampleData {
public string Name { get; set; }
}
public static class DataCache {
private static readonly Dictionary<string, object> CacheDict
= new Dictionary<string, object>();
private static readonly object Locker = new object();
public static T Get<T>(string key, Func<T> getSampleData) {
if (!CacheDict.ContainsKey(key)) {
lock (Locker)
if (!CacheDict.ContainsKey(key)) {
CacheDict.Add(key, getSampleData());
}
}
return (T)CacheDict[key];
}
}
public class Program {
private static SampleData CreateSampleData() {
return new SampleData() { Name = "Piotr Sowa" };
}
private static void Main(string[] args)
{
SampleData data = DataCache.Get("Author", CreateSampleData);
}
}
}
Regards
I'm working on this project and I have a large number of "services" to execute. I need to have this service codes so I can verifiy and make decisions according to selected services by it's code.
I'm tring to define this enum and get its values from the web.config so I can change them easily.
public enum ServiceCodes
{
Transfer= Convert.ToInt32(ConfigurationManager.AppSettings["servTransfer"]),
ChangePlate= Convert.ToInt32(ConfigurationManager.AppSettings["servChangePlate"])
}
But I get this error: The expression must be constant. (because of the Conversion)
What can I do? (Or you can suggest me totally different approaches.)
Just go ahead and define a static class like this:
public static class ServiceCodes
{
readonly static int Transfer = Convert.ToInt32(ConfigurationManager.AppSettings["servTransfer"])
//...
}
The documentation states that enum values are constant. An alternative approach is to declare a class with static readonly members.
If you still need the type safety provided by an enum, you could use a slightly complex approach:
public class ServiceCodes {
public static readonly ServiceCodes Transfer = new ServiceCodes(Convert.ToInt32(ConfigurationManager.AppSettings["servTransfer"]));
public static readonly ServiceCodes ChangePlate = new ServiceCodes(Convert.ToInt32(ConfigurationManager.AppSettings["servChangePlate"]));
internal int Code {get; private set;}
private ServiceCodes(int code) {
Code = code;
}
}
Then, a method like:
public void SomeAction(ServiceCodes serviceCode) {
//....
}
could be called like this:
SomeAction(ServiceCodes.Transfer);
But, given the complexity (compared with the gain), I would go with the first approach.