C# inherit from Dictionary, iterate over KeyValuePairs - c#

I have a class that inherits from Dictionary<string, string>. Within an instance method, I want to iterate over all KeyValuePair<string, string>'s. I've tried doing the following:
foreach (KeyValuePair<string, string> pair in base)
But this fails with the following error:
Use of keyword 'base' is not valid in this context
How can I iterate over the KeyValuePair<string, string>'s in an instance method in a class that derives from Dictionary<string, string>?
Edit: I found I can do the following:
var enumerator = base.GetEnumerator();
while (enumerator.MoveNext())
{
KeyValuePair<string, string> pair = enumerator.Current;
}
However, I would still like to know if there's a way to do this via a foreach loop.
Edit: thanks for the advice about not inheriting from Dictionary<string, string>. I'm instead implementing System.Collections.IEnumerable, ICollection<KeyValuePair<string, string>>, IEnumerable<KeyValuePair<string, string>>, IDictionary<string, string>.

First, deriving from the .NET collection classes is generally ill-advised because they don't offer virtual methods for calls not inherited from object. This can result in bugs when passing your derived collection in via a base-class reference somewhere. You are better off implementing the IDictionary<T,TKey> interface and aggregating a Dictionary<,> inside your implementation - to which you then forward the appropriate calls.
That aside, in your specific case, what you want to do is:
foreach( KeyValuePair<string,string> pair in this ) { /* code here */ }
The base keyword is primarily used to access specific members of your base class. That's not what you're doing here - you are attempting to iterate over the items of a particular instance ... which is simply the this reference.

I agree with JaredPar's comment that this isn't a great idea. You probably don't want to publicly expose all of the methods of Dictionary to the outside world, so just make the Dictionary a private member variable and then provide your own interface to it.
With that said, the way to do what you're trying to do is:
foreach (KeyValuePair<string, string> pair in this)

Encapsulate Dictionary<string, string> as a composed field inside custom class MyDictionary and implement a custom IEnumerable and IEnumerator (or variations thereof) for MyDictionary (or make a method that implements handy C# yield keyword to produce the items)...
E.g.
class MyDictionary : IEnumerable<KeyValuePair<string,string>> {
Dictionary<string, string> _dict;
IEnumerator<KeyValuePair<string,string>> GetEnumerator() {
return new MyEnum(this); // use your enumerator
// OR simply forget your own implementation and
return _dict.GetEnumerator();
}
class MyEnum : IEnumerator<KeyValuePair<string,string>> {
internal MyEnum(MyDictionary dict) {
//... dict
}
// implemented methods (.MoveNext, .Reset, .Current)...
This maintains encapsulation of extraneous methods.
And you can still iterate over you instances like so from inside or outside:
// from outside
MyDictionary mdict = new MyDictionary();
foreach (KeyValuePair<string, string> kvp in mdict)
//...
// from inside, assuming: this == MyDictionary instance)
public void MyDictionaryMethod() {
foreach (KeyValuePair<string, string> kvp in this)
//...

Related

Could not access the First() method when inherited a class from Dictionary in C#

I can normally use the First() method in a Dictionary type variable like this,
Dictionary<string,string> dic = new Dictionary<string,string>();
dic.Remove(dic.First(kvp => kvp.Value == some_value).Key);
However, when I tried to inherit a class from Dictionary like below it's giving me an error.
class Dic : Dictionary<string, string>
{
public void DoSomething()
{
Remove(First(kvp => kvp.Value == some_value).Key);
}
}
This is the error I'm getting.
BTW, First() originates not from Dictionary
I have tried implementing IEnumerable but it did not help
Your Dic class won't compile as-is. You can use this instead of dic in the class.
class Dic : Dictionary<string, string>
{
public void DoSomething()
{
Remove(this.First(kvp => kvp.Value == "some_value").Key);
}
}
And then call like this: new Dic().DoSomething();
Why I am inheriting it is because I need to override the default Add() method to check whether the Value is already existing. –
That's what the TryAdd method (either as an extension method or baked in depending on versions) is for. If TryAdd returns false, the value existed; it does not throw an error on duplicate add. You can also use ContainsKey and TryGetValue as alternative ways of checking if a key exists or not

How can I refactor this C# code currently using Dictionarys to have even less redundancy and be more typesafe?

Because of business decisions which are above my paygrade, I need to parse and merge multiple XML files.
In order to cut down on redundant code, I have this map:
private static readonly Dictionary<string, Type> listTypeByFileName = new Dictionary<string, Type> {
{"a.xml", typeof(List<A>)},
{"b.xml", typeof(List<B>)},
{"c.xml", typeof(List<C>)},
{"d.xml", typeof(List<D>)},
// etc.
};
Because how this map gets used, after downloading and parsing all the XMLs, the result is of type Dictionary<string, object> where the key is the same as the keys in the above map and the value is of the type specified in the map, as result of executing this code with DownloadFiles(config):
private static Dictionary<string, object> DownloadFiles(IConfigurationRoot config) {
Dictionary<string, object> dataListByFileNames = new Dictionary<string, object>();
listTypeByFileName.Keys.ToList()
.ForEach(name => dataListByFileNames.Add(name, DownloadData(name, config)));
return dataListByFileNames;
}
private static object DownloadData(string name, IConfigurationRoot config) {
_ = listTypeByFileName.TryGetValue(name, out Type listType);
return new XmlSerializer(listType, new XmlRootAttribute("Document"))
.Deserialize(new StringReader(DownloadFromBlobStorage(name, config).ToString()));
}
private static CloudBlockBlob DownloadFromBlobStorage(string filetoDownload, IConfigurationRoot config) {
return CloudStorageAccount.Parse(config["AzureWebJobsStorage"])
.CreateCloudBlobClient()
.GetContainerReference(config["BlobStorageContainerName"])
.GetBlockBlobReference(filetoDownload);
First question: Is there a way I can make the return more typesafe? Perhaps using parameterized types?
The second part of the problem is actually consuming this Dictionary.
For each type in this Dictionary, I now need a function like:
private void AddA(Dictionary<string, object> dataByFileNames) {
if (dataByFileNames.TryGetValue("a.xml", out object data)) {
List<A> aList = (List<A>)data;
aList.ForEach(a =>
doSomethingWithA(a);
);
}
}
private void AddB(Dictionary<string, object> dataByFileNames) {
if (dataByFileNames.TryGetValue("b.xml", out object data)) {
List<B> bList = (List<B>)data;
bList.ForEach(b =>
doSomethingWithB(b);
);
}
}
// etc.
As I already have the list of filenames to types (top of this question), I feel there should be some way to abstract the above so it does not need to be repeated again and again and again.
Note, it may be significant that every type (A, B, C, D, etc. all have a property string Id which will be definitely be needed for all doStringWithX() methods... if useful, I can create an interface to get this. It is okay if I need to caste to the correct type within each doStringWithX() or when invoking each of these methods.c
First, instead of storing the List<T> type in the dictionary, just store the underlying generic type:
private static readonly Dictionary<string, Type> listTypeByFileName = new Dictionary<string, Type> {
{"a.xml", typeof(A)},
{"b.xml", typeof(B)}
// etc.
That's going to make future steps a little bit easier. When deserializing, create the generic list type. After getting the type from the dictionary, you can do:
var listType = typeof(List<>).MakeGenericType(typeRetrievedFromDictionary);
Once you've deserialized it, cast it as IList. That's effectively casting it as a list of object. That's okay. Because you deserialized using a specific type, every item in the list will be of the expected type.
Create a dictionary for the type-safe methods you want to invoke on every time in list.
Dictionary<Type, Action<object>> methodsToInvokeByType;
Add methods to the dictionary:
doSometingMethods.Add(typeof(A), dataItem => DoSomethingWithA((A)dataItem));
doSometingMethods.Add(typeof(B), dataItem => DoSomethingWithB((B)dataItem));
Now, once you've got your IList full of objects, you retrieve the type-safe method to invoke:
var methodToInvoke = methodsToInvokeByType[typeRetrievedFromDictionary];
Then do this:
foreach(object itemInList in list) // this is your deserialized list cast as IList
{
methodToInvoke(itemInList);
}
So if the type is A, you'll be invoking
DoSomethingWithA((A)itemInList)
It's not pretty. Bridging between code that uses objects and Type and type-safe generic code can be messy. But ultimately the goal is that whatever those final methods are - DoSomethingWithA, DoSomethingWithB, etc., at least those are type-safe.
You can simplify some more:
Create a class that deserializes a list and passes it off to a method for processing, and an interface:
public interface IXmlFileProcessor
{
void Process(byte[] xmlFile);
}
public class XmlFileProcessor<T> : IXmlFileProcessor
{
private readonly Action<T> _doSomething;
public XmlFileProcessor(Action<T> doSomething)
{
_doSomething = doSomething;
}
public void Process(byte[] xmlFile) // or string or whatever
{
// deserialize into a List<T>
foreach (T item in deserializedList)
_doSomething(item);
}
}
Then create a Dictionary<Type, IXmlFileProcessor> and populate it:
fileProcessors.Add(typeof(A), new XmlFileProcessor<A>(SomeClass.DoSomethingWithA));
fileProcessors.Add(typeof(B), new XmlFileProcessor<B>(SomeClass.DoSomethingWithB));
That approach (injecting the Action) is intended to keep the "do something" method decoupled from the class responsible for deserialization. DoSomething could also be a generic method in XmlFileProcessor<T>. There are different ways to compose those classes and add them to that dictionary. But either way, having determined the type, you just retrieve the correct type-specific processor from the dictionary, pass your file to it, and it does the rest.
That approach bridges the generic/non-generic gap by making the class - XmlFileProcessor<T> - generic, but having it implement a non-generic interface. It works as long as you take steps (using the dictionary) to ensure that you're selecting the correct implementation for whatever type you're deserializing.

Is it possible to create a method that returns one of two possible types?

I have 2 data structures: Dictionary<string, string> and Multimap<string, string>.
Multimap is really just a Dictionary under the hood. I took must of the code from this question. Here's the class definition:
public class Multimap<TKey, TValue> : Dictionary<TKey, HashSet<TValue>>
{ ... }
Both data structures have a .Add(TKey key, TValue value) method.
I have a class that is responsible for populating these maps from certain files. I currently have the following two methods:
public Dictionary<string, string> PopulateDictionary(...)
{
Dictionary<string, string> returnDictionary = new Dictionary<string, string>();
...
foreach (...)
{
...
returnDictionary.Add(key, value);
}
return returnDictionary;
}
public Multimap<string, string> PopulateMultimap(...)
{
Multimap<string, string> returnMultimap = new Multimap<string, string>();
...
foreach (...)
{
...
returnMultimap.Add(key, value);
}
return returnMultimap;
}
As you can see, they're exactly the same, both around 25 lines long, and the only difference is their return type. What I am looking to do is condense this into one method.
My first attempt was to have the method
public Dictionary<string, object> PopulateGenericDictionary(...)
{ ... }
Where object was either string or HashSet<string>. But I didn't have much luck casting from Dictionary<string, object> to Multimap<string, string>.
Extracting the logic out of the methods is an option, but it's not great. Because of the foreach loops, there's always going to be some logic inside the two methods. You do end up with methods that are twice as small, but there's still two identical methods, which doesn't truly solve the problem.
This would be my ideal method structure:
public Dictionary<string, string> PopulateDictionary(...)
{
return MethodThatDoesAllTheLogic(...);
}
public Multimap<string, string> PopulateMultimap(...)
{
return MethodThatDoesAllTheLogic(...);
}
public ??? MethodThatDoesAllTheLogic(...)
{ ... }
I've been fiddling around with casting and generics, but I just can't get it to work. Any ideas?
Edit
I have used millimoose's solution. Here's my code now:
public Dictionary<string, string> GenerateDictionary(...)
{
Dictionary<string, string> returnMap = new Dictionary<string, string>();
PopulateDictionary(returnMap.Add, ...);
return returnMap;
}
public Multimap<string, string> GenerateMultimap(...)
{
Multimap<string, string> returnMap = new Multimap<string, string>();
PopulateDictionary(returnMap.Add, ...);
return returnMap;
}
private static void PopulateGenericDictionary(Action<string, string> addFunc, ...)
{
...
foreach (...)
{
addFunc(key, value);
}
}
Much cleaner!
To work around the lack of a common interface, you can invent one ad-hoc using a bunch of delegate type parameters:
void MethodThatDoesAllTheLogic(Action<string, string> addFunc)
{
// ...
addFunc(key, value);
// ...
}
public Dictionary<...> PopulateDictionary()
{
// ...
MethodThatDoesAllTheLogic(result.Add);
}
(Adding more parameters as necessary.)
I would avoid having the helper method create the actual collection at all; have it just populate an existing collection. That can be done much more effectively, since the Add method has the same signature in both cases. We can just use a delegate to accept the Add method:
public static void PopulateMapping<TKey, TValue>(Action<TKey, TValue> addMethod,
IEnumerable<TKey> data) //include other parameters needed to populate the data
{
foreach (var key in data)
{
addMethod(key, default(TValue));
}
}
Then it would be used like this:
public static Dictionary<string, string> PopulateDictionary()
{
Dictionary<string, string> output = new Dictionary<string, string>();
PopulateMapping<string, string>(output.Add, new string[] { "a" });
return output;
}
If you are only looking for an Add method, then both objects should share IDictionary. However, that Add method only uses objects. That is probably the closest that you can get without having to use generics in the method...but again you lose the benefits of generics at that point.
See if this approach will be useful:
The key is to make abstraction on creation of the object (Dictionary or Multimap) and aquiring the values - the two differences in the populating method.
public Dictionary<string, TValue> Populate<TValue>( Dictionary<string, TValue> returnDict, Func<SomeType, TValue> valueProvider)
{
string key = null;
...
foreach (...)
{
...
returnDict.Add(key, valueProvider(value));
}
return returnDict;
}
The example invocation is can be:
public void Test()
{
Populate(new Multimap<string, HashSet<string>>(), (t) => new HashSet<HashSet<string>>());
}
I'm not sure if the valueProvider delegate will be suited to your problem. Try to give more information about it.
If your inner logic is truly identical except for what type TValue is - and I mean word-for-word identical - then you could do something like:
IDictionary<string, TValue> MethodThatDoesAllTheLogic<TValue>(whatever)
{
// word for word-identical logic
}
I made the method take TValue as its only type parameter because that's the only difference (in the example you showed): both methods have string as the first type parameter.
ETA: This assumes that MultiMap implements IDictionary<K,V>. Since you said that it inherited from Dictionary<K,V> I assumed that it did.
in C# with generics you can require them to extend or implement a specific class in our case Dictionary, the following is how you might achieve that.
public T Populate<T>(string val) where T : Dictionary<string, string>, new()
{
T returnDict = new T();
returnDict.Add("key", "val");
return returnDict;
}

problem with dictionary class

I saw this article on msdn with the example http://msdn.microsoft.com/en-us/library/xfhwa508.aspx
So i decided to give it a shot and try this out in my wpf application:
Dictionary<string, string> Dictionarycheck =
new Dictionary<string, string>();
Dictionarycheck.Add("demo1");
Why this won't work? I get the error: Invalid token '(' in class, struct, or interface member declaration
Two problems:
You can't just add a key to a dictionary. You have to add a key/value pair
You can't include statements directly in a class declaration - they have to be in constructors/methods/properties/etc. This is the direct cause of your problem, given your error message.
In other words, you've probably got something like this:
public class Test
{
Dictionary<string, string> Dictionarycheck =
new Dictionary<string, string>();
Dictionarycheck.Add("demo1");
}
when it should be something like this:
public class Test
{
public void DemoMethod()
{
Dictionary<string, string> dictionaryCheck =
new Dictionary<string, string>();
dictionaryCheck.Add("demo1", "value1");
}
}
(I've adjusted the name of the variable for convention, too.)
Dictionary(TKey, TValue)
So its Dictionarycheck.Add("Key", "Value");
You are probably writing the code outside of a method (like I just did to test it). Further, Dictionary.Add has two arguments.

C# object initializer wanting to use wrong Add method

I have the following class hierarchy:
public class Row : ICloneable, IComparable, IEquatable<Row>,
IStringIndexable, IDictionary<string, string>,
ICollection<KeyValuePair<string, string>>,
IEnumerable<KeyValuePair<string, string>>,
System.Collections.IEnumerable
{ }
public class SpecificRow : Row, IXmlSerializable,
System.Collections.IEnumerable
{
public void Add(KeyValuePair<MyEnum, string> item) { }
}
However, trying to do the following gives an error:
var result = new SpecificRow
{
{MyEnum.Value, ""},
{MyEnum.OtherValue, ""}
};
I get this error:
The best overloaded Add method 'Row.Add(string, string)' for the collection initializer has some invalid arguments
How can I make it so that using an object initializer on the derived class SpecificRow allows type MyEnum? It seems like it should see the Add method in SpecificRow.
Update: I implemented an extra interface on SpecificRow so it now looks like this:
public class SpecificRow : Row, IXmlSerializable,
System.Collections.IEnumerable,
ICollection<KeyValuePair<MyEnum, string>>
{ }
However, I still get the same Add error. I'm going to try implementing IDictionary<MyEnum, string> next.
A collection initializer does not necessarily look at any ICollection.Add(x) method. More specifically, for a collection initializer
new SpecificRow {
{ ? }
}
C# looks at any Add method with signature Add(?); if ? contains comma's, C# looks at an Add method with multiple arguments. The compiler does not have any special handling of KeyValuePair<,> at all. The reason { string, string } works, is because your base class has an overload Add(string, string), and not because it has an overload for Add(KeyValuePair<string, string>).
So to support your syntax for
new SpecificRow {
{ MyEnum.Value, "" }
};
you need an overload of the form
void Add(MyEnum key, string value)
That's all there is to it.
It looks like it's because you're only implementing IDictionary<string, string>, and all the other interfaces associated with it. Your Add(KeyValuePair<MyEnum, string>) method isn't implementing any interface member, it's just another member of the SpecificRow class, which happens to be named Add, which is why it is getting ignored.
You should be able to do one of the following, depending on what your requirements are:
Implement IDictionary<MyEnum, string> in addition to IDictionary<MyEnum, string>, including the dependent interfaces (ICollection<KeyValuePair<MyEnum, string>>, etc).
Implement IDictionary<MyEnum, string> instead of IDictionary<MyEnum, string>, again including the dependent interfaces.
Change the declaration of Row to Row<T>, and implement IDictionary<T, string>, including the dependent interfaces. SpecificRow would then implement Row<MyEnum> instead of just Row.
Ruben's answer is definitely the best, but if you didn't want to add Add(MyEnum key, string value) then you could also initialize the collection like so:
var result = new SpecificRow
{
new KeyValuePair<MyEnum, string>(MyEnum.Value, ""}),
new KeyValuePair<MyEnum, string>(MyEnum.OtherValue, ""})
};

Categories