How can I document the key usage in a Dictionary so that it shows in Visual studio when coding using that object?
I'm looking for something like:
/// <param name="SpecialName">That Special Name</param>
public Dictionary<string,string> bar;
So far the best attempt has been to write my own class:
public class SpecialNameDictionary : IDictionary<string, string>
{
private Dictionary<string, string> data = new Dictionary<string, string>();
/// <param name="specialName">That Special Name</param>
public string this[string specialName]
{
get
{
return data[specialName];
}
}
}
But It adds a lot of code that doesn't do anything. Additionally I must retype every Dictionary method to make it compile.
Is there a better way to achive the above?
You can define, dictionary like this:
public class SpecialNameDictionary : Dictionary<string, string>
{
/// <param name="specialName">That Special Name</param>
public new string this[string specialName]
{
get
{
return base[specialName];
}
}
}
Instead of deriving from IDictionary derive from Dictionary, and make new implementation of indexer.
Document the field like this:
/// <summary>
/// A map from the special name to the frongulator.
/// </summary>
public Dictionary<string,string> bar;
(I assume that in reality it's either not public or not a field - but the same would apply for private fields or public properties.)
You won't get IntelliSense on the indexer itself, but any usage of bar should make it reasonably clear.
Three other alternatives:
Use types which make the usage clearer (a string could be anything, but a FrongulatorSpecialName is clearer)
Make the name of the field/property itself clearer
Hide the dictionary, but add a method such as "GetFrongulatorBySpecialName"
You could inherit directly from Dictionary<> instead of IDictionary<>, that way you only need to re-implement the indexer.
Related
I'm using .NET Core and want to configure my options. Based on this sample class
public class MySettings
{
public Dictionary<int, string> Mapping { get; set; }
}
I added a mapping to the appsettings.json file
{
"MySettings": {
"Mapping": {
"9454545": "agf51528gfhdfg",
"13544": "bfds28745hfghsdfghd"
}
}
}
but as you can see the keys are of type string, not integers. It is not possible for me to change the property names to numbers because then I get the error
JSON property names should be strings. Numeric property names are not
allowed in JSON
So when calling this in the startup file
IConfigurationSection mySettingsSection = configuration.GetSection("MySettings");
services.Configure<MySettings>(mySettingsSection);
the field Mapping will have 0 items because it is not able to parse the JSON keys to integers automatically. Is there a way I can tell .NET Core to parse those string keys to integers and fill up the dictionary?
Hmm I think you need workaround because cannot allow int at json file.
Something like this:
var mySettingsSection =
configuration.GetSection("MySettings").Get<Dictionary<string, string>>().Select(x => new KeyValuePair<int, string>(int.Parse(x.Key), x.Value));
Is there a way I can tell .NET Core to parse those string keys to integers and fill up the dictionary?
Unfortunately, there is no way to achieve that.
Reason
First, internally ConfigurationProvider use a IDictionary<string, string> to hold key-value pair of configuration settings (source code).
/// <summary>
/// The configuration key value pairs for this provider.
/// </summary>
protected IDictionary<string, string> Data { get; set; }
Second, when JsonConfigurationProvider try to parse JSON file (appsettings.json in your case) and populate that Data property, the parsing result, without doubt, is IDictionary<string, string> type (source code).
public static IDictionary<string, string> Parse(Stream input)
=> new JsonConfigurationFileParser().ParseStream(input);
private IDictionary<string, string> ParseStream(Stream input)
{
// Exclude implementation
}
Last, when you call services.Configure<MySettings>(mySettingsSection), under the hood it calls Bind method to populate MySettings object by matching property name (string) against configuration key (string) (source code).
/// <summary>
/// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.
/// </summary>
/// <param name="configuration">The configuration instance to bind.</param>
/// <param name="instance">The object to bind.</param>
/// <param name="configureOptions">Configures the binder options.</param>
public static void Bind(this IConfiguration configuration, object instance, Action<BinderOptions> configureOptions)
So, inside .NET Core framework, from building configuration key-value pairs to binding configuration to custom object, the data flow is always around IDictionary<string, string> type. Moreover, there is no custom binding options to let you do any kind of data type conversion.
You have two options,
First one is to change the config file to int,string. For example:
{
"MySettings": {
"Mapping": {
1: "a",
2: "b"
}
}
}
Second possibility is to create another properties in MySettings class that will be with correct type (Dictionary<string,string>) and in Mapping field you can do a kind of conversion in the getter.
public class MySettings
{
public Dictionary<int, string> Mapping => Config.ToDictionary(a => Int32.Parse(a.Key), a => a.Value);
public Dictionary<int, string> Config { get; set; }
}
If you will call Mapping multiple type you can save the conversion result once and not recalculate it in every call.
I want to use generics in combination with MEF-"[ImportMany(....))]", but I get compile-errors.
The following code without using generics works fine: The factory-class "HelperOneFactory" (see below) is searching für all implementations of the "IHelperOne"-interface. From this list it takes the first one without Metadata-value "Original". If it doesn't exist, it takes the first without checking the Metadata-value.
/// =====================================================================================
/// factory-implementation for interface-type "IHelperOne" (==> works fine)
/// =====================================================================================
[Export(typeof(HelperOneFactory))]
public class HelperOneFactory: IPartImportsSatisfiedNotification
{
[ImportMany(typeof(IHelperOne))]
private IEnumerable<Lazy<IHelperOne, Dictionary<string, object>>> Helper;
/// <summary>
/// reference to the relevant implementaion (HelperOneOriginal or HelperOneCusto)
/// </summary>
public IHelperOne Current { get; private set; }
/// <summary>
/// looking for all implementations of IHelperOne to find out the one to use
/// </summary>
public void OnImportsSatisfied()
{
Current = Helper.Count() > 1 ? Helper.First<Lazy<IHelperOne, Dictionary<string, object>>>(s => !s.Metadata.ContainsValue("Original")).Value :
Helper.First<Lazy<IHelperOne, Dictionary<string, object>>>().Value;
}
That works fine, but I have to implement factory-classes for many interface-types. So I try to use generics for the interface-type, but then I get compile-errors (using .NET Framework 4.6.1):
/// =====================================================================================
/// a version with using generic ==> compiler-errors !!
/// =====================================================================================
[Export(typeof(HelperTemplateFactory<>))]
public class HelperTemplateFactory<THelperInterface> : IPartImportsSatisfiedNotification
{
[ImportMany(typeof(THelperInterface))] // ==> ERROR: "Attribute argument cannot use type parameters"
[ImportMany(THelperInterface)] // ==> also ERROR: "Type parameter name is not valid at this point"
private IEnumerable<Lazy<THelperInterface, Dictionary<string, object>>> Helper;
...
Is it possible to use a generic type for the "ImportMany" command?
The context of the problem:
A "normal" class "HelperOneOriginal" is the Standard-version of the HelperOne and can be overwritten in Custom-Projects by defining a sub-class "HelperOneCustom", normaly placed in a separate VisualStudio-Project.
Both classes have the interface "IHelperOne".
The main-Program should use the Custom-class if defined, or otherwise the Original-class. The Original-class has the Metadata-Information "Orginal".
Therefore the "HelperOneFactory" looks for all realisations of "IHelperOne"-Interfaces and takes the first one without Metadata "Original".
If it doesn't exist it takes the Original-class. The reference to the relevant class ist stored inside the HelperClass in the member "Current" for using by the main-program.
If necessary a small test-project can be provided.
I suggest, I have to write an "answer" to mark the question als "resolved" =>
a line with only "[ImportMany]" is the solution!
Thanks to Dave M
I am trying to access some specific variable that are only available in a child class. But the problem is that I recieve the parent of this class by parameter. Even with casting I can't seem to be able to access the members. Can it be done?
public class ENUMTranslator : ITranslate<RedisData>
{
public string Translate(RedisData message)
{
string bitMask = message.AssociatedParam.ParamDictionary["Bitmask"];
var enumerations = (EnumParams)message.AssociatedParam.EnumDictionary
}
}
The thing is that the data is not in message itself but inside AssociatedParam Which is the parent class of EnumParams.
The EnumDictionary is what I am trying to access that should be in EnumParams, but I just can't access it.
EDIT : Here is the EnumParam class.
message.AssociatedParams
is a GAPParam
public class EnumParams : GAPParam
{
#region Class Members
/// <summary>
/// Dictionary for the enums linking name with hex value
/// </summary>
private Dictionary<string, string> _enumDictionary;
#endregion // Class Members
#region Properties
/// <summary>
/// Dictionary for the enums linking name with hex value
/// </summary>
public Dictionary<string, string> EnumDictionary
{
get { return _enumDictionary; }
set { _enumDictionary = value; }
}
#endregion // Properties
#region Constructor
/// <summary>
/// Initialise the dictionaries
/// </summary>
public EnumParams()
{
_enumDictionary = new Dictionary<string, string>();
}
#endregion // Constructor
}
I cannot see it with intellisense and it would not compile either.
Well you could cast message.AssociatedParam to an EnumParams:
var enumerations = ((EnumParams)message.AssociatedParam).EnumDictionary
but if message.AssociatedParam is not castable to an EnumParams then it will fail at runtime. Some way to mitigate the risk:
should EnumDictionary be on GAPParam instead? Even if it's virtual or abstract?
should message.AssociatedParam be an EnumDictionary instead of a GAPParam?
do a check before casting to make sure message.AssociatedParam is an EnumParams - but then what do you do if its not?
I have a shared object between threads that is used to hold file state information. The object that holds the information is this class:
/// <summary>
/// A synchronized dictionary class.
/// Uses ReaderWriterLockSlim to handle locking. The dictionary does not allow recursion by enumeration. It is purly used for quick read access.
/// </summary>
/// <typeparam name="T">Type that is going to be kept.</typeparam>
public sealed class SynchronizedDictionary<U,T> : IEnumerable<T>
{
private System.Threading.ReaderWriterLockSlim _lock = new System.Threading.ReaderWriterLockSlim();
private Dictionary<U, T> _collection = null;
public SynchronizedDictionary()
{
_collection = new Dictionary<U, T>();
}
/// <summary>
/// if getting:
/// Enters read lock.
/// Tries to get the value.
///
/// if setting:
/// Enters write lock.
/// Tries to set value.
/// </summary>
/// <param name="key">The key to fetch the value with.</param>
/// <returns>Object of T</returns>
public T this[U key]
{
get
{
_lock.EnterReadLock();
try
{
return _collection[key];
}
finally
{
_lock.ExitReadLock();
}
}
set
{
Add(key, value);
}
}
/// <summary>
/// Enters write lock.
/// Removes key from collection
/// </summary>
/// <param name="key">Key to remove.</param>
public void Remove(U key)
{
_lock.EnterWriteLock();
try
{
_collection.Remove(key);
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary>
/// Enters write lock.
/// Adds value to the collection if key does not exists.
/// </summary>
/// <param name="key">Key to add.</param>
/// <param name="value">Value to add.</param>
private void Add(U key, T value)
{
_lock.EnterWriteLock();
if (!_collection.ContainsKey(key))
{
try
{
_collection[key] = value;
}
finally
{
_lock.ExitWriteLock();
}
}
}
/// <summary>
/// Collection does not support iteration.
/// </summary>
/// <returns>Throw NotSupportedException</returns>
public IEnumerator<T> GetEnumerator()
{
throw new NotSupportedException();
}
/// <summary>
/// Collection does not support iteration.
/// </summary>
/// <returns>Throw NotSupportedException</returns>
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotSupportedException();
}
}
I call this dictionary like this:
SynchronizedDictionary _cache = new SynchronizedDictionary();
Other threads can be spawned and use the thread like this:
_cache["key"];
The dictionary can be modified at runtime. I see no problem here. Or am I wrong?
The problem, in my eyes, lies in the enumerator, because I want to make an enumerator that iterates over the collection. How do I do this? I have thought of these three solutions:
Making a Enumerator like this:
http://www.codeproject.com/Articles/56575/Thread-safe-enumeration-in-C
(but using ReaderWriterLockSlim)
Expose the lock object, like SyncRoot does (but with
ReaderWriterLockSlim), so a caller calls the enter and exit read methods.
Use a database (SQLite fx) instead, holding the information.
The problem with number 1) is:
it uses the contructor to entry read mode. What if the
GetEnumerator() is call manually, not using the foreach? And forget
calling dispose.
I do not know if this is a good coding style. Even though I like the
code.
If the caller uses a foreach, I do not know what the caller might do
between the instantiation of the enumerator and the call to dispose.
If I have understood the documentation I have read correct this can
end up blocking the writer as long as there is one reader left doing
some heavy work.
The problem with number 2) is:
I do not like exposing this. I know that the .NET API does it, but
do not like it.
It is up to the caller to enter and exit properly
There is no problem with 3) I my eyes. But I am doing this small project as a spare time project and I want to learn more about multi-threading and reflection, so I want to keep this as a last option.
The reason why I want to iterate over the collection at runtime is that I want to find the values, that matches some criteria.
Maybe it is just me that have invented a problem?
I know of ConcurrentDictionary, but I do not want to use this. I am using this project as a playground. Playing with threading and reflection.
EDIT
I have been asked what it is that I am reading and writing. And I am going to tell this in this edit. I am reading and writing this class:
public class AssemblyInformation
{
public string FilePath { get; private set; }
public string Name { get; private set; }
public AssemblyInformation(string filePath, string name)
{
FilePath = filePath;
Name = name;
}
}
I am doing alot of reads, and almost no writes at runtime. Maybe I will do 2000 and 1 write. There is not going to be alot of object either, maybe 200.
I'll treat your questions as a request for feedback which helps you learn. Let me address the three solutions you have already identified:
Yes, this is why such a design should never be exposed as an API to a 3rd-party (or even other developers). It is tricky to use correctly. This codeproject article has some nasty advice.
Much better because this model would be explicit about locking, not implicit. However this violates separation of concerns in my opinion.
Not sure what you mean here. You could have a Snapshot() method on your dictionary which does a read-only copy which can be safely passed around and read. This is a different trade-off than solution 1.
There is a different solution entirely: Use an immutable dictionary. Such a dictionary could be passed around, read and enumerated safely even under concurrent write access. Such dictionaries/maps are commonly implemented using trees.
I'll elaborate more on a key point: You need to think about the concurrent system as a whole. You cannot make you app correct by making all components thread-safe (in your case a dictionary). You need to define, what you are using the dictionary for.
You say:
The reason why I want to iterate over the collection at runtime is
that I want to find the values, that matches some criteria.
You you have concurrent writes happening to the data and want to get a consistent snapshot atomically from the dictionary (maybe to shot some progress report in the UI?). Now that we know this goal, we can devise a solution:
You could add a Clone method to your dictionary which clones all data while taking the read-lock. This will give the caller a fresh object which it can then enumerate over independently. This would be a clean and safely exposable API.
Instead of implementing IEnumerable directly I would add a Values property (like Dictionary.Values):
public IEnumerable<T> Values {
get {
_lock.EnterReadLock();
try {
foreach (T v in _collection.Values) {
yield return v;
}
} finally {
_lock.ExitReadLock();
}
}
}
I have a dictionary of strings that i want the user to be able to add/remove info from then store it for them so it they can access it the next time the program restarts
I am unclear on how i can store a dictionary as a setting. I see that under system.collections.special there is a thing called a stringdictionary but ive read that SD are outdated and shouldn't be used.
also in the future i may have need to store a dictionary that is not strings only (int string)
how would you store a dictionary in the settings file for a .net application?
You can use this class derived from StringDictionary. To be useful for application settings it implements IXmlSerializable.
Or you can use similar approach to implement your own XmlSerializable class.
public class SerializableStringDictionary : System.Collections.Specialized.StringDictionary, System.Xml.Serialization.IXmlSerializable
{
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
while (reader.Read() &&
!(reader.NodeType == System.Xml.XmlNodeType.EndElement && reader.LocalName == this.GetType().Name))
{
var name = reader["Name"];
if (name == null)
throw new FormatException();
var value = reader["Value"];
this[name] = value;
}
}
public void WriteXml(System.Xml.XmlWriter writer)
{
foreach (System.Collections.DictionaryEntry entry in this)
{
writer.WriteStartElement("Pair");
writer.WriteAttributeString("Name", (string)entry.Key);
writer.WriteAttributeString("Value", (string)entry.Value);
writer.WriteEndElement();
}
}
}
Resulting XML fragment will look similar to:
...
<setting name="PluginSettings" serializeAs="Xml">
<value>
<SerializableStringDictionary>
<Pair Name="property1" Value="True" />
<Pair Name="property2" Value="05/01/2011 0:00:00" />
</SerializableStringDictionary>
</value>
</setting>
...
The simplest answer would be to use a row & column delimiter to convert your dictionary to a single string. Then you just need to store 1 string in the settings file.
If you don't need to use the settings designer or edit your settings with a text editor, you can create a simple class that derives from ApplicationSettingsBase:
namespace MyNamespace
{
using System.Collections.Generic;
using System.Configuration;
/// <summary>
/// Persistent store for my parameters.
/// </summary>
public class MySettings : ApplicationSettingsBase
{
/// <summary>
/// The instance lock.
/// </summary>
private static readonly object InstanceLock = new object();
/// <summary>
/// The instance.
/// </summary>
private static MySettings instance;
/// <summary>
/// Prevents a default instance of the <see cref="MySettings"/> class
/// from being created.
/// </summary>
private MySettings()
{
// don't need to do anything
}
/// <summary>
/// Gets the singleton.
/// </summary>
public static MySettings Instance
{
get
{
lock (InstanceLock)
{
if (instance == null)
{
instance = new MySettings();
}
}
return instance;
}
}
/// <summary>
/// Gets or sets the parameters.
/// </summary>
[UserScopedSetting]
[SettingsSerializeAs(SettingsSerializeAs.Binary)]
public Dictionary<string, string> Parameters
{
get
{
return (Dictionary<string, string>)this["Parameters"];
}
set
{
this["Parameters"] = value;
}
}
}
}
The real trick is the [SettingsSerializeAs(SettingsSerializeAs.Binary)] attribute. Most (all?) classes can get serialized this way where SettingsSerializeAs.String or SettingsSerializeAs.Xml wont work for a Dictionary.
Use this in your code as you would normal settings:
// this code untested...
MySettings.Instance.Parameters["foo"] = "bar";
MySettings.Instance.Parameters.Save();
MySettings.Instance.Parameters.Reload();
string bar;
if (!MySettings.Instance.Parameters.TryGetValue("foo", out bar))
{
throw new Exception("Foobar");
}
If you want the Dictionary to serialize into something user editable, you must derive from Dictionary and play with TypeConverter (see Using Custom Classes with Application Settings).
Other than doing something like David's suggests, I would look into alternate storage for the Dictionary. Ultimately the Settings object serializes to disk.
Have you considered using XML to store your dictionary? That would provide a certain amount of extensibility if in the future you decide you want to be able to store other types of dictionaries. You might do something like:
<dictionary>
<entry key="myKey">
[whatever data you like]
</entry>
</dictionary>
Might be overkill, but you'd also be prepared in the case that you wanted to store more complex data, like custom objects.
You can also use a System.Collections.Specialized.StringCollection by putting key on even index and values on odd index.
/// <summary>
/// Emulate a Dictionary (Serialization pb)
/// </summary>
private static string getValue(System.Collections.Specialized.StringCollection list, string key)
{
for (int i = 0; i * 2 < list.Count; i++)
{
if (list[i] == key)
{
return list[i + 1];
}
}
return null;
}
/// <summary>
/// Emulate a Dictionary (Serialization pb)
/// </summary>
private static void setValue(System.Collections.Specialized.StringCollection list, string key, string value)
{
for (int i = 0; i * 2 < list.Count; i++)
{
if (list[i] == key)
{
list[i + 1] = value;
return;
}
}
list.Add(key);
list.Add(value);
}
You could create a custom class that exposes a Dictionary as a public property. Then you can specify this custom type as the type for your setting.
Edit:
I have just read that, for some reason, a generic dictionary cannot be XML-serialized, so my solution will probably not work (I haven't tested it though...). That's strange, because a generic list can be serialized without any problem.
You could still create a custom class that can be set as a user setting, but you will need to have a list exposed as a property instead of a dictionary.
Edit: This will return a Hashtable (for whatever reason, despite being a 'DictionarySectionHandler'). However, being that Hashtables and Dictionaries are so similar, it shouldn't be a large issue (though I realize Dictionaries are newer, parameterized, etc; I would have preferred dicitonaries myself, but this is what .NET gives us).
The best answer I just found for this is here. It returns a typesafe collection witout any muddling in code to transform it, and you create an obvious (and simple) collection in your .config file. I'm using this and it's quite straight forward for any future programmer (including yourself). It allows for stronger typing and more flexibility, without any overly-complicated and unnecessary parsing.
You can store a StringCollection. It is similar to this solution.
I made 2 extension methods to convert between StringCollection and a Dictionary. This is the easiest way I could think of.
public static class Extender
{
public static Dictionary<string, string> ToDictionary(this StringCollection sc)
{
if (sc.Count % 2 != 0) throw new InvalidDataException("Broken dictionary");
var dic = new Dictionary<string, string>();
for (var i = 0; i < sc.Count; i += 2)
{
dic.Add(sc[i], sc[i + 1]);
}
return dic;
}
public static StringCollection ToStringCollection(this Dictionary<string, string> dic)
{
var sc = new StringCollection();
foreach (var d in dic)
{
sc.Add(d.Key);
sc.Add(d.Value);
}
return sc;
}
}
class Program
{
static void Main(string[] args)
{
//var sc = new StringCollection();
//sc.Add("Key01");
//sc.Add("Val01");
//sc.Add("Key02");
//sc.Add("Val02");
var sc = Settings.Default.SC;
var dic = sc.ToDictionary();
var sc2 = dic.ToStringCollection();
Settings.Default.SC = sc2;
Settings.Default.Save();
}
}