How to implement nested dictionaries?

S

Siegfried Heintze

This program works except for line 58. I tried to solve the problem by
implementing my own indexer as you can see on line 27.
It says object reference not set to an instance of an object. Use the "new
keyword to create an object instance.

I tried using new but that does not work for built-in or primitive types.
Can anyone help me modify my program so I can create new elements using this
C++ like syntax:

cons["a"]["b"] = "c";

I know I can do it the old manual way and you can see that I have done that
on line 57.

Thanks,
siegfried

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
/* 5*/
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Reflection;
/**
/* 10*/
* Begin commands to execute this file using MS.NET with bash
* csc /out:program.exe /checked /d:noprompt /debug Program.cs
* ./Program hello there <<EOF
* insert your data here
* EOF
/* 15*/
* rm Program.exe
* rm Program.pdb
* End commands to execute this file using MS.NET with bash
*/

/* 20*/
namespace DeleteMe_Test004_2D_HashTables
{
[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue>
: Dictionary<TKey, TValue>
/* 25*/
{
new public TValue this[TKey index]
{
get
{
/* 30*/
try
{
TValue v = base[index];
return v;
}
/* 35*/
catch (System.Collections.Generic.KeyNotFoundException e)
{
TValue tmp=default(TValue);
base.Add(index, tmp);
return tmp;
/* 40*/
}
}
set
{
base[index] = value;
/* 45*/
}
}
}

public class SD<K, V> : SerializableDictionary<K, V> { }
/* 50*/
public class Program {
static System.IO.TextWriter outp = System.Console.Out;
public static void Main(string[] args){
try{
SD<string, SD<string, string>> cons=new
SD<string,SD<string,string>>();/* 55*/
cons.Add("a",new SD<string,string>());
cons["a"].Add("b", "hello");
cons["qrs"]["tuv"] = "world";
} finally {
}
/* 60*/
}
}
}
 
P

Peter Duniho

This program works except for line 58. I tried to solve the problem by
implementing my own indexer as you can see on line 27.
It says object reference not set to an instance of an object. Use the
"new
keyword to create an object instance.

I tried using new but that does not work for built-in or primitive types.
Can anyone help me modify my program so I can create new elements using
this
C++ like syntax:

cons["a"]["b"] = "c";

I don't understand what about that syntax is supposed to be "C++ like",
other than the aspect of the syntax that is both "C++ like" and "C# like"
at the same time.

As far as the question goes, the problem is that you are initializing a
missing value using "default(TValue)", which for any reference type is
"null". So of course when you then return that value, you get the null
reference exception.

The correct way to initialize it is to initialize the missing value with
"new TValue()", just as the exception message suggests. To be able to do
that, you'll have to specify the "new" constraint for your generic class:

public class SerializableDictionary<TKey, TValue> : Dictionary<TKey,
TValue> where TValue : new()
{
// ...
}

Pete
 
S

Siegfried Heintze

This program works except for line 58. I tried to solve the problem by
implementing my own indexer as you can see on line 27.
It says object reference not set to an instance of an object. Use the
"new
keyword to create an object instance.

I tried using new but that does not work for built-in or primitive
types.
Can anyone help me modify my program so I can create new elements using
this
C++ like syntax:

cons["a"]["b"] = "c";

I don't understand what about that syntax is supposed to be "C++ like",
other than the aspect of the syntax that is both "C++ like" and "C# like"
at the same time.[/QUOTE]

The C++ std::map class will initialize an element as necessary.
As far as the question goes, the problem is that you are initializing a
missing value using "default(TValue)", which for any reference type is
"null". So of course when you then return that value, you get the null
reference exception.

The correct way to initialize it is to initialize the missing value with
"new TValue()", just as the exception message suggests. To be able to do
that, you'll have to specify the "new" constraint for your generic class:

public class SerializableDictionary<TKey, TValue> : Dictionary<TKey,
TValue> where TValue : new()
{
// ...
}

I'm trying that but Visual Studio is giving me syntax errors on the comma:

public class SerializableDictionary<TKey, TValue> : Dictionary<TKey,
TValue> where TValue : new(), IXmlSerializable
{
// ...
}


Thanks,
Siegfried
 
S

Siegfried Heintze

When in doubt, read the documentation (or the compiler error). The
"new()" constraint is required to always be the last constraint given.

http://msdn.microsoft.com/en-us/library/sd2w2ew5.aspx
Sorry -- I should have been more explicit. Yes, I had already discovered
that the new constraint must be the last one thru trial and error. However,
when I move IXMLSerializable to be the first ancestor my references to
"base" do not work.

How do I explicitly call the functions of Dictionary if it is not the first
ancestor? I need to call the indexer function of Dictionary inside my
indexer function.

Thanks,
Siegfried
 
P

Peter Duniho

Sorry -- I should have been more explicit.

You _still_ should be more explicit.
Yes, I had already discovered
that the new constraint must be the last one thru trial and error.
However,
when I move IXMLSerializable to be the first ancestor my references to
"base" do not work.

What do you mean by "ancestor"? The only code example you've posted so
far, IXMLSerializable isn't an ancestor, it's a constraint.

And what do you mean by "do not work"? In what way does your reference to
"base" "not work"?
How do I explicitly call the functions of Dictionary if it is not the
first
ancestor?

What do you mean by "it"? In the sentence you wrote, "it" would refer to
"Dictionary" (that being the precedent noun). But in the only code
I need to call the indexer function of Dictionary inside my
indexer function.

And why can't you?

If you're going to ask why your code doesn't work, you need to post the
code as well as describe what about the code doesn't work. Just saying
"it doesn't work" and not showing any code provides no useful information
to anyone who might answer.

Pete
 
S

Siegfried Heintze

Sorry about the communication. I'm in the habit of refering to
IXmlSerializable as an ancestor interface and Dictionary as
ancestor implementation. Would you prefer I use the word constraint
instead of ancestor interface? That would be a new use of the term
constraint for me.

I'm getting a syntax error on the last comma line line 16. When I put
IXmlSerializable first on line 16 then line 25 causes a syntax error. And
what about line 30 -- I think that needs to be changed to.

001 using System;
002 using System.Collections.Generic;
003 using System.Linq;
004 using System.Text;
005 using System.Runtime.InteropServices;
006 using System.IO;
007 using System.Xml;
008 using System.Xml.Serialization;
009 using System.Reflection;
010
011 namespace DeleteMe_Test004_2D_HashTables
012 {
013 // http://weblogs.asp.net/pwelter34/archive/2006/05/03/444961.aspx
014 [XmlRoot("dictionary")]
015 public class SerializableDictionary<TKey, TValue>
016 : Dictionary<TKey, TValue> where TValue: new(),
IXmlSerializable
017 {
018 #region indexer members
019 new public TValue this[TKey index]
020 {
021 get
022 {
023 try
024 {
025 TValue v = base[index];
026 return v;
027 }
028 catch (System.Collections.Generic.KeyNotFoundException
e)
029 {
030 TValue tmp=default(TValue);
031 base.Add(index, tmp);
032 return tmp;
033 }
034 }
035 set
036 {
037 base[index] = value;
038 }
039 }
040 #endregion
041 #region IXmlSerializable Members
042 public System.Xml.Schema.XmlSchema GetSchema() { return null; }
043 public void ReadXml(System.Xml.XmlReader reader) {
044 XmlSerializer keySerializer = new
XmlSerializer(typeof(TKey));
045 XmlSerializer valueSerializer = new
XmlSerializer(typeof(TValue));
046
047 bool wasEmpty = reader.IsEmptyElement;
048 reader.Read();
049 if (wasEmpty)
050 return;
051 while (reader.NodeType !=
System.Xml.XmlNodeType.EndElement)
052 {
053 reader.ReadStartElement("item");
054 reader.ReadStartElement("key");
055 TKey key = (TKey)keySerializer.Deserialize(reader);
056 reader.ReadEndElement();
057 reader.ReadStartElement("value");
058 TValue value =
(TValue)valueSerializer.Deserialize(reader);
059 reader.ReadEndElement();
060 this.Add(key, value);
061 reader.ReadEndElement();
062 reader.MoveToContent();
063 }
064 reader.ReadEndElement();
065 }
066
067 public void WriteXml(System.Xml.XmlWriter writer)
068 {
069 XmlSerializer keySerializer = new
XmlSerializer(typeof(TKey));
070 XmlSerializer valueSerializer = new
XmlSerializer(typeof(TValue));
071 foreach (TKey key in this.Keys)
072 {
073 writer.WriteStartElement("item");
074 writer.WriteStartElement("key");
075 keySerializer.Serialize(writer, key);
076 writer.WriteEndElement();
077 writer.WriteStartElement("value");
078 TValue value = this[key];
079 valueSerializer.Serialize(writer, value);
080 writer.WriteEndElement();
081 writer.WriteEndElement();
082 }
083 }
084 #endregion
085 }
086
087 public class SD<K, V> : SerializableDictionary<K, V> { }
088 public class Program {
089 [DllImport("msvcrt.dll", SetLastError = true)]
090 static extern int _getch();
091 static System.IO.TextWriter outp = System.Console.Out;
092 static System.IO.TextReader inp = System.Console.In;
093 public static void Main(string[] args){
094 try{
095 SD<string, SD<string, string>> cons=new
SD<string,SD<string,string>>();
096 cons.Add("a",new SD<string,string>());
097 cons["a"].Add("b", "hello");
098 cons["qrs"]["tuv"] = "world";
099 foreach (string src in cons.Keys)
100 foreach (string dst in cons[src].Keys)
101 outp.WriteLine("cons[" + src + "]" + "[" + dst + "]=" +
cons[src][dst]);
102
103 XmlSerializer sr = new XmlSerializer(typeof(SD<string,
SD<string, string>>));
104 SD<string, string> cons2=new SD<string,string>();
105 cons2["abcd"]= "tuvxyz";
106 XmlSerializer sr2 = new XmlSerializer(typeof(SD<string,
string>));
107 sr2.Serialize(outp, cons2);
108 sr.Serialize(outp, cons);
109 } finally {
110 }
111 }
112 }
113 }
114
 
S

Siegfried Heintze

Thanks Pete! Now we are getting somewhere!

The problem (syntax error: string does not have a zero parameter
constructor) is on line 98 and it occurs because I'm trying to nest
dictionaries and I guess string does not match the new constraint!

SD<string, SD<string, string>> cons =new
SD<string,SD<string,string>>();

So is there a way I can have my cake and eat it too? Or do I have to have
two nearly identical map classes, one with the new constraint and one that
accomodates primitives?

Thanks,
Siegfried




using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices; /* 5*/
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Reflection;
/** /* 10*/
* Begin commands to execute this file using MS.NET with bash
* csc /out:program.exe /checked /d:noprompt /debug Program.cs
* ./Program hello there <<EOF
* insert your data here
* EOF /* 15*/
* rm Program.exe
* rm Program.pdb
* End commands to execute this file using MS.NET with bash
*/
/* 20*/
namespace DeleteMe_Test004_2D_HashTables {
// http://weblogs.asp.net/pwelter34/archive/2006/05/03/444961.aspx
[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue>
: Dictionary<TKey, TValue> /* 25*/
, IXmlSerializable where TValue : new()
{
#region indexer members
new public TValue this[TKey index] {
get { /* 30*/
try {
TValue v = base[index];
return v;
}
catch ( /* 35*/
System.Collections.Generic.KeyNotFoundException) {
TValue tmp=new TValue();
base.Add(index, tmp);
return tmp;
} /* 40*/
}
set { base[index] = value; }
}
#endregion
#region IXmlSerializable Members /* 45*/
public System.Xml.Schema.XmlSchema GetSchema() { return null; }
public void ReadXml(System.Xml.XmlReader reader) {
XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
XmlSerializer valueSerializer
= new XmlSerializer(typeof(TValue)); /* 50*/
bool wasEmpty = reader.IsEmptyElement;
reader.Read();
if (wasEmpty) return;
while (reader.NodeType != System.Xml.XmlNodeType.EndElement) {
reader.ReadStartElement("item"); /* 55*/
reader.ReadStartElement("key");
TKey key = (TKey)keySerializer.Deserialize(reader);
reader.ReadEndElement();
reader.ReadStartElement("value");
TValue value /* 60*/
= (TValue)valueSerializer.Deserialize(reader);
reader.ReadEndElement();
this.Add(key, value);
reader.ReadEndElement();
reader.MoveToContent(); /* 65*/
}
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer) {
XmlSerializer keySerializer /* 70*/
= new XmlSerializer(typeof(TKey));
XmlSerializer valueSerializer
= new XmlSerializer(typeof(TValue));
foreach (TKey key in this.Keys) {
writer.WriteStartElement("item"); /* 75*/
writer.WriteStartElement("key");
keySerializer.Serialize(writer, key);
writer.WriteEndElement();
writer.WriteStartElement("value");
TValue value = this[key]; /* 80*/
valueSerializer.Serialize(writer, value);
writer.WriteEndElement();
writer.WriteEndElement();
}
} /* 85*/
#endregion
}

public class SD<K, V> :
SerializableDictionary<K, V> where V : new() { } /* 90*/
public class Program {
[DllImport("msvcrt.dll", SetLastError = true)]
static extern int _getch();
static System.IO.TextWriter outp = System.Console.Out;
static System.IO.TextReader inp = System.Console.In;/* 95*/
public static void Main(string[] args){
try{
SD<string, SD<string, string>> cons
=new SD<string,SD<string,string>>();
cons.Add("a",new SD<string,string>()); /* 100*/
cons["a"].Add("b", "hello");
cons["qrs"]["tuv"] = "world";
foreach (string src in cons.Keys)
foreach (string dst in cons[src].Keys)
outp.WriteLine("cons[" /* 105*/
+ src + "]"
+ "[" + dst + "]="
+ cons[src][dst]);
XmlSerializer sr
= new XmlSerializer( /* 110*/
typeof(SD<string, SD<string, string>>));
SD<string, string> cons2=new SD<string,string>();
cons2["abcd"]= "tuvxyz";
XmlSerializer sr2 = new XmlSerializer(typeof(SD<string, string>));
sr2.Serialize(outp, cons2); /* 115*/
sr.Serialize(outp, cons);
} finally {
#if noprompt
outp.WriteLine("terminating Program.cs");
#else /* 120*/
outp.Write("Enter any key to exit Program.cs: ");
_getch();
#endif
}
} /* 125*/
}
}
 
S

Siegfried Heintze

Pete!
That works!

Assuming I stick with the indexer syntax, how would I implement a triply
nested map?

new SD<string,SD<string,SD<string,string>>>((()=>new
SD<string,SD<string,string>>(()=>new SD<string,string>())), (()=>new
SD<string,string>())));

Oh that hurts my brain! I think I would have to change my member delegate
from a scalar to an array of delegates and then some how, I'd have to know
the integer index to index into the array of delegates when calling my Make
delegete? Ooops, but that would not work either since the return type of
each delegate would be different.
Hmm.... I don't know how this would work!

Any suggestions for implementing nested maps of arbitrary depths where the
nesting depth is frozen at run time and I can use the syntax

xyz["abc"]["def"]["xyz"]["tuv"]="hello";

Thanks,
Siegfried


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
/* 5*/
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Reflection;
/**
/* 10*/
* Begin commands to execute this file using MS.NET with bash
* csc /out:program.exe /checked /d:noprompt /debug Program.cs
* ./Program hello there <<EOF
* insert your data here
* EOF
/* 15*/
* rm Program.exe
* rm Program.pdb
* End commands to execute this file using MS.NET with bash
*/

/* 20*/
namespace DeleteMe_Test004_2D_HashTables {
// http://weblogs.asp.net/pwelter34/archive/2006/05/03/444961.aspx
[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue>
: Dictionary<TKey, TValue>
/* 25*/
, IXmlSerializable //where TValue : new()
{
public delegate TValue MakeValueDelegate();
public MakeValueDelegate MakeValue;// = (() => default(TValue));
public SerializableDictionary() { MakeValue = (() =>
default(TValue)); }/* 30*/
public SerializableDictionary(MakeValueDelegate m){MakeValue = m;}
#region indexer members
new public TValue this[TKey index] {
get {
try
{ /* 35*/
TValue v = base[index];
return v;
}
catch (
System.Collections.Generic.KeyNotFoundException)
{ /* 40*/
TValue tmp=this.MakeValue();
base.Add(index, tmp);
return tmp;
}
}
/* 45*/
set { base[index] = value; }
}
#endregion
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema() { return null; }
/* 50*/
public void ReadXml(System.Xml.XmlReader reader) {
XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
XmlSerializer valueSerializer
= new XmlSerializer(typeof(TValue));
bool wasEmpty = reader.IsEmptyElement;
/* 55*/
reader.Read();
if (wasEmpty) return;
while (reader.NodeType != System.Xml.XmlNodeType.EndElement) {
reader.ReadStartElement("item");
reader.ReadStartElement("key");
/* 60*/
TKey key = (TKey)keySerializer.Deserialize(reader);
reader.ReadEndElement();
reader.ReadStartElement("value");
TValue value
= (TValue)valueSerializer.Deserialize(reader);
/* 65*/
reader.ReadEndElement();
this.Add(key, value);
reader.ReadEndElement();
reader.MoveToContent();
}
/* 70*/
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer) {
XmlSerializer keySerializer
= new XmlSerializer(typeof(TKey));
/* 75*/
XmlSerializer valueSerializer
= new XmlSerializer(typeof(TValue));
foreach (TKey key in this.Keys) {
writer.WriteStartElement("item");
writer.WriteStartElement("key");
/* 80*/
keySerializer.Serialize(writer, key);
writer.WriteEndElement();
writer.WriteStartElement("value");
TValue value = this[key];
valueSerializer.Serialize(writer, value);
/* 85*/
writer.WriteEndElement();
writer.WriteEndElement();
}
}
#endregion
/* 90*/
}

public class SD<K, V> :
SerializableDictionary<K, V> //where V : new()
{
/* 95*/
public SD(MakeValueDelegate m) : base(m) { }
public SD() { }
}
public class Program {
[DllImport("msvcrt.dll", SetLastError = true)]
/* 100*/
static extern int _getch();
static System.IO.TextWriter outp = System.Console.Out;
static System.IO.TextReader inp = System.Console.In;
public static void Main(string[] args){
try{
/* 105*/
SD<string, SD<string, string>> cons
=new SD<string,SD<string,string>>(()=>new SD<string,string>());
cons.Add("a",new SD<string,string>());
cons["a"].Add("b", "hello");
cons["qrs"]["tuv"] = "world";
/* 110*/
foreach (string src in cons.Keys)
foreach (string dst in cons[src].Keys)
outp.WriteLine("cons["
+ src + "]"
+ "[" + dst + "]="
/* 115*/
+ cons[src][dst]);
XmlSerializer sr
= new XmlSerializer(
typeof(SD<string, SD<string, string>>));
sr.Serialize(outp, cons);
/* 120*/
} finally {
#if noprompt
outp.WriteLine("terminating Program.cs");
#else
outp.Write("Enter any key to exit Program.cs: ");
/* 125*/
_getch();
#endif
}
}
}
/* 130*/
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top