C# Expose strongly typed dictionary keys - c#

I am sure I am not going to use the correct words to explain but I hope you understand what I am trying to accomplish.
I have a class that gets passed in dictionary collection. Is it mandatory that this passed in dictionary collection has certain keys that this class needs. I would like to be able to expose publicly those keys needed by the class, but not just as strings, but actual items. An enumeration would work but since this class is a base class I dont think that is a good solution. I need something similar to the Resources class that is generated everytime you add items to the Resources.resx file. So you that you can just go to Resources.Resources.MyLabel1, Resources.Resources.MyLabel2, etc.
Is this possible?
Thanks,
Mike

Create properties that expose your values
public class MyClass
private Dictionary<string,string> _dict;
public MyClass (Dictionary<string,string> dict)
{
_dict = dict;
}
public string FirstName { get { return _dict["FirstName"]; } }
public string LastName { get { return _dict["LastName"]; } }
...
}
Alternatively you can aslo create read/write properties
public string FirstName {
get { return _dict["FirstName"]; }
set { _dict["FirstName"] = value; }
}

You could just offer a static or const member on your class:
class PickyClass {
public static readonly string[] RequiredKeys = new[] {"length", "width"};
/// <summary>
/// Please note that you must include at least RequiredKeys in values
/// </summary>
public void Setup(Dictionary<string,string> values)
{
...
}
}
(edit) or is it important that they are addressable by name? In which case, how about:
class PickyClass {
public class RequiredKeys
{
public const string Length = "length";
public const string Width = "width";
}
/// <summary>
/// Please note that you must include every const in RequiredKeys in values
/// </summary>
public void Setup(Dictionary<string,string> values)
{
...
}
}

This sounds like a detail you should hide
public void Setup(Dictionary<string,string> values)
If values must contain certain keys, then maybe you could encode that information in your own dictionary type.
public class FussyDictionary : Dictionary<string,string> {
// You must provide these values
public FussyDictionary(string valueA, string valueB) {
Add("magicKey1", valueA);
Add("magicKey2", valueB);
}
// Override remove to stop you removing these keys?
}

Related

Can static values be read from a file?

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.

Subclassing dictionary for use in external components

I have a dictionary class which is used to store data, and which tracks hundreds of variables during the course of a session.
I have been tasked with building a common framework, which I can reference this base set of functionality, but allow the calling components to use different key and value within the dictionary.
Currently, this dictionary uses a 4-part tuple as the key, and a 2-part value.
The two components I'm tying into has a different key and value layout.
Component 1 - key is a 5-part tuple, and value is a 3-part.
Component 2 - Key is a 3-part tuple (string, int, string) and the same 2-part value.
This class handles data transfer to other components, so to avoid duplication of effort, want to keep as much of the common functionality in the Common dll, and external components would use the Survey class which the different key/value. Not certain I'm explaining it well enough.
I have included the current code below.
Seems to me, if the main Survey is created with object, object, and subclass the external components with the correct key/value pair.
public sealed class Survey
{
#region Private Objects
private Survey()
{
}
private Dictionary<SurveyKey, SurveyValue> survey = new Dictionary<SurveyKey, SurveyValue>();
private int maxLines = 50000;
private bool AllowLogging { get => (survey.Count > maxLines); }
#endregion
private void WriteData(SurveyKey key, SurveyValue value)
{
if (AllowLogging)
{
if (!survey.ContainsKey(key))
survey.Add(key, value);
else
survey[key] = value;
}
}
}
#region SurveyValue Class
public sealed class SurveyValue
{
public SurveyValue(int? value = null, string detail = null)
{
Detail = detail;
Value = value;
}
// Uses an either/or value; Value or Detail
public string Detail { get; private set; }
public int? Value { get; private set; }
}
#endregion
#region SurveyKey Class
public sealed class SurveyKey : Tuple<string, string, string, string>
{
public SurveyKey(string Signal, string SignalType, string Name, string OverallType) : base(Signal, SignalType, Name, OverallType) { }
public string Signal { get => Item1; }
public string SignalType { get => Item2; }
public string Name { get => Item3; }
public string OverallType { get => Item4; }
}
Make your common class generic of type K,V and use the where keyword to restrict the dictionary K and V to KBaseClass and VBaseClass. Component1 can expose KBaseClass and VBaseClass derived types and inherit from common or reuse common.
Turns out, I was over thinking this problem. All I need to do is create my base dictionary as Survey, and use this in my external components.
For some reason I was thinkning I needed to create an interface to allow the plugging in of the base dictionary.
private Dictionary<TKey, TValue> survey = new Dictionary<TKey, TValue>();

C# - add attribute like '.Length' to custom class

I've playing around with a class that acts as a public interface for a private List<T> attribute. I noticed that the List<> class has an attribute Length that tells you how many elements it contains.
This is an attribute you cannot alter, and on the intellisense appears with an image of a spanner next to it. It is not a method as it does not require () after coding the name.
I've seen attributes of this type before, but never used them in my own classes. Does anybody have any idea how I can replicate Length in my custom class?
Thanks,
Mark
It's a property with no setter. If you're wrapping a List<T> you can just use it's Count as your own:
public int Count {get {return _myPrivateList.Count; } }
If you're using C# 6, you can use this:
public int Count => _myPrivateList.Count;
If you currently have a class that contains a List, then you can take advantage of the Count property already present on it by exposing a property that simply uses that :
public class YourExampleList<T>
{
// Example of your inner list
private List<T> _list { get; set; }
// Use the Count property to expose a public "Length" equivalent
public int Length { get { return _list.Count; } }
}
This is actually not a method, but a property.
So you could have define in your class
private List<string> myList = new List<string>();
public int NumberOfElements
{
get { return this.myList.Count; }
}
A normal property would be defined such as
public bool ColumnNames { get; set; }
List<T> myList = new List<T>();
Now you can create your own implementation on your custom class. Something like:
public int Length {get {return myList.Count; }}
I must admit that your question is a bit vague. It sounds like you want know how to create a read only attribute / property. This can be achieved by creating a property wrapper for a private field member of your class as follow:
class MyCustomClass
{
private int _length;
public int Length
{
get { return _length; }
}
}
Say for example you have a class like this:
public class MyClass
{
private string _str;
public MyClass()
{
_str = "Sample String";
}
public int Length
{
get
{
return _str.Length;
}
}
}
This is what's happening:
We're declaring a private field at the start of the class named _str.
In the constructor we're then assigning it a value of "Sample String".
After the constructor we're then declaring the public attribute Length of type int, and only giving it a get accessor. Like your example, this only allows the value to be read, and not set.
Within the get we then tell it to return the value of _str's length.
Using code similar to this you can implement a Length attribute for any custom class.

How do you make an 'enum' that has data tied to it?

I have a Vote class and one of the properties it can have is a vote type. Such as unanimous, a 3/4 vote, a simply majority, etc. Each type needs to have a string associated with it which will describe the vote type (like "A simply majority requires 51% to pass" etc.). I need to pass these vote types/description in with my view model to my view and then I can make my drop down list with it.
Then, when the form that creates the vote is submitted I just need to bind the vote type (without description) to the Vote model (which is part of the view model).
I've only been using C# for a short time and I don't quite understand how the enums work in it. Perhaps enum is not the way to go about this.
public class VoteViewModel
{
public VoteViewModel()
{
Vote = new Vote();
}
public Vote Vote { get; set; }
public int EligibleVoters { get; set; }
}
And this is where I'll be putting the drop down.
<section class="vote-type">
<select name="">
<option value="">Select Vote Type</option>
</select>
<section class="vote-type-info">
<p class="vote-rules">To pass this vote, at least 51% of Eligible Voters must vote to approve it.</p>
</section>
</section>
Please notice I'm only showing for strings for it could be any type. In each case I mention how to extend it for more values if possible.
Using the enum as a key
You can use your enum type as a key for a dictionary (you want to be unique, so make it static and readonly in some helper class):
private static readonly Dictionary<MyEnum, string> _dict =
{
//Using dictionary initialization
{MyEnum.MyValue, "The text for MyValue"},
{MyEnum.MyOtherValue, "Some other text"},
{MyEnum.YetAnotherValue, "Something else"}
}
public static readonly Dictionary<MyEnum, string> Dict
{
get
{
return _dict;
}
}
And access the associated value:
string text = Dict[MyEnum.MyValue];
Or with:
string text;
if (Dict.TryGetValue(MyEnum.MyValue, out text))
{
//It has the value
}
else
{
//It doesn't have the value
}
This way you can access a string that is associated with the enum value. Then you can expose your Dictionary so that you can read the corresponding values.
You will need a complex type for storing more than one value. Just use your custom type isntead of string. Or if available you can use Tuples.
Accesing the Dictionary may mean an extra annoyance and hopefully it will not mean a threading problem too.
Enum.GetName
You can use Enum.GetName to read the name of the values of your enum:
string text = Enum.GetName(MyEnum.MyValue);
//text will have the text "MyValue"
//or
var some = MyEnum.MyValue;
string text = Enum.GetName(some);
Note: ToString() should work too.
Sadly, this will not work for something else than the string.
Also it has the drawback that you cannot put any text there (it has to be a valid identifier).
Custom Attributes
You will have to declare an attribute type:
[AttributeUsage(AttributeTargets.Field)]
public class EnumValueAttribute : System.Attribute
{
public readonly string _value;
public string Value
{
get
{
return _value;
}
}
public EnumValueAttribute(string value) // value is a positional parameter
{
//beware: value can be null...
// ...but we don't want to throw exceptions here
_value = value;
}
}
Now you apply the attribute to your enum:
public enum MyEnum
{
[EnumValue("The text for MyValue")]
MyValue = 1,
[EnumValue("Some other text")]
MyOtherValue = 2,
[EnumValue("Something else")]
YetAnotherValue = 3
}
Lastly you will need to read the attribute back:
public static string GetValue(MyEnum enumValue)
{
FieldInfo fieldInfo = typeof(MyEnum).GetField(enumValue.ToString());
if (!ReferenceEquals(fieldInfo, null))
{
object[] attributes = fieldInfo.GetCustomAttributes(typeof(EnumValueAttribute), true);
if (!ReferenceEquals(attributes, null) && attributes.Length > 0)
{
return ((EnumValueAttribute)attributes[0]).Value;
}
}
//Not valid value or it didn't have the attribute
return null;
}
Now you can call it:
string text1 = GetValue(MyEnum.MyValue);
//text1 will have the text "MyValue"
//or
var some = MyEnum.MyValue;
string text2 = GetValue(some);
You can add more fields to your attribute class and use them to pass any other value you may need.
But this requires reflexion, and it may not be available if you are running in a sandbox. Also it will retrieve the attributes each time, creating some short lived objects in the proccess.
Emulate Enum
You can emulate an enum with a sealed class that has no public constructor and exposes static readonly instances of itself:
public sealed class MyEnumEmu
{
private static readonly string myValue = new MyEnumEmu("The text for MyValue");
private static readonly string myOtherValue = new MyEnumEmu("Some other text");
private static readonly string yetAnotherValue = new MyEnumEmu("Something else");
public static MyEnumEmu MyValue
{
get
{
return myValue;
}
}
public static MyEnumEmu MyOtherValue
{
get
{
return myOtherValue;
}
}
public static MyEnumEmu YetAnotherValue
{
get
{
return yetAnotherValue;
}
}
private string _value;
private MyEnumEmu(string value)
{
//Really, we are in control of the callers of this constructor...
//... but, just for good measure:
if (value == null)
{
throw new ArgumentNullException("value");
}
else
{
_value = value;
}
}
public string Value
{
get
{
return _value;
}
}
}
Use it as always:
var some = MyEnumEmu.MyValue;
And access the associated value:
string text = MyEnumEmu.MyValue.Value;
//text will have the text "MyValue"
//or
string text = some.Value;
This is the more flexible of all, you can either use a complex type instead of string or add extra fields for passing more than a single value.
But... it is not really an enum.
You could create a "constant" dictionary (or rather readonly static, since you can't create a constant dictionary) around your Enum.
public enum VoteType { Unanimous = 1, SimpleMajority = 2, ... }
public static readonly Dictionary<VoteType, string> VoteDescriptions = new Dictionary<VoteType, string>
{
{ VoteType.Unanimous, "Unanimous description" },
{ VoteType.SimpleMajority, "Simple majority" },
...
};
public class Vote()
{
public VoteType VoteSelectType { get; set; }
}
public enum VoteType
{
[Display(Name = "Enter Text Here")]
unanimous = 1,
[Display(Name = "Enter Text Here")]
threequatervote = 2,
[Display(Name = "Enter Text Here")]
simplymajority = 3
}
Goto here this is pretty much your solution
How do I populate a dropdownlist with enum values?
You can use enums if you want but you need to decide how to make the link between the enum value and what you want to display. For example, an enum value of SimpleMajority you would want displayed as "Simple Majority". One way to do this is using the Description attribute and a helper class as described here.
However, you might find it easier to set up a lightweight collection class to store vote type values and their description. This could be as simple as a Dictionary<int, string> You will probably find this a more straightforward approach.
Since you have the type and description I'll better suggest you to create a class that wraps up both instead of enum. The advantage is you can reduce more work and it's very flexible.
public class VoteType
{
public string Name{ get; set; }
public string Description{ get; set; }
}
Now your Vote class will have reference to this VoteType.
public class Vote
{
...
public VoteType Type{ get; set; }
}
In your VoteViewModel you better have a class that contains all the VoteTypes.
public class VoteViewModel
{
...
public IEnumerable<SelectListItem> VoteTypes{ get; set; }
}
Now you can easily bind the VoteTypes in a dropdownlist.
#model VoteViewModel
#Html.DropDiwnListFor(m => m.VoteTypes,...)
I have used this before, it is really handy.
http://www.codeproject.com/Articles/13821/Adding-Descriptions-to-your-Enumerations
In short what it lets you do is:
public enum MyColors{
[Description("The Color of my skin")]
White,
[Description("Bulls like this color")]
Red,
[Description("The color of slime")]
Green
}
and then get the description back by simply calling:
String desc = GetDescription(MyColor.Green);
It does use reflection though, so there is a tradeoff between simplicity and a slight performance hit. Most of the time I'd take the performance hit...

How do I make an arraylist public

In File1 I created a class with 3 strings. I created another class with a public arraylist. I want this arraylist to be dynamic and the object it contains are the class with the 3 strings.
I can access the members of the class in the file but not in a separate file.
file1
public class SensorCollection
{
public string ipAddress;
public string portNumber;
public string physicalLocation;
public DetectorCollection(string ipAddr, string portNum, string loc)
{
this.ipAddress = ipAddr;
this.portNumber = portNum;
this.physicalLocation = loc;
}
}
public class SensorCollectionArray
{
public System.Collections.ArrayList SensorArrayList;
}
...
System.Collections.ArrayList DetectorArrayList = new System.Collections.ArrayList();
...
DetectorArrayList.Add(new DetectorCollection(ipAddress, portNum, str));
So I can fill the array of classes but can't access it in a separate file.
File 2
AdvancedSettingsForm.SensorCollectionArray mainDetectorCollectionArray;
System.Collections.ArrayList arrList;
If you create a SensorCollectionArray like this:
SensorCollectionArray mySCA = new SensorCollectionArray();
Then you can access it's ArrayList like this (for example, to add an item):
mySCA.SensorArrayList.Add(mySensorCollection);
Note however, that in the code you've posted, you didn't include a constructor for the SensorCollectionArray, so the SensorArrayList will be null after instantiation. So you can either set it to a separately instantiated ArrayList, or you can create the ArrayList within your SensorCollectionArray class.
Final note: You might want to look into the generic List(of T) class if you want to create a strongly typed collection
Not entirely sure what're attempting to do, but I assume it's something like the below. Presumably, you're creating a collection of your sensors because you want to apply some rules of some kind before storing it to the collection.
"Is this a good sensor? It is? Add it to the collection!"
Otherwise, you could just use a
List<Sensor> mySensors;
and not really use a class that'll essentially doing the same things. Aside from that, like it's been mentioned there's not really a reason to use ArrayList. As Marc points out here, the most compelling reason to use ArrayList is if you're using .NET 1.1; otherwise, you should use the generic List collection and all the great things it does for you.
//Sensor.cs
public class Sensor
{
public string Ip{ get; set; }
public string Port{ get; set; }
public string PhysicalLocation{ get; set }
public Sensor(string ipAddr, string portNum, string loc)
{
Ip= ipAddr;
Port= portNum;
PhysicalLocation= loc;
}
}
//SensorCollection.cs
public class SensorCollection
{
private List<Sensor> sensors;
public Sensor this[int i]
{
get { return this.sensors[i]; }
set { this.sensors[i] = value; }
}
public IEnumerable<Sensor> Sensors
{
get{ return this.sensors; }
}
public SensorCollection()
{
sensors = new List<Sensor>();
}
public SensorCollection(string ip, string port, string location) : this()
{
this.sensors.Add(new Sensor(ip, port, location));
}
public SensorCollection(Sensor sensor) : this()
{
this.sensors.Add(sensor);
}
public void AddSensor(Sensor sensor)
{
//Determine whether or not to add it
this.sensors.Add(sensor);
}
public void RemoveSensor(Sensor sensor)
{
if (sensors.Contains(sensor))
sensors.Remove(sensor);
}
}
Edit
How do I access the ipaddress of each sensor in my dynamically created
list of classes?
var mySensors = new SensorCollection();
mySensors.AddSensor(new Sensor("1.1.1.1", "123", "Home"));
mySensors.AddSensor(new Sensor("9.9.9.9", "123", "Work"));
foreach(Sensor s in mySensors.Sensors)
Console.WriteLine(s.Ip);
I can not seem to access the members of the class in another file
Make sure they're in the same namespace, or that you include a "using" statement that includes the namespace of your classes you create.

Categories

Resources