Anonymous classes and implicitly typed arrays make
code shorter by doing away with the need for class
templates and explicit types in source code. A big drawback of this feature is elements are read-only.
No additional code is missing from this example, except to paste it into your source file.
A concise, anonymous data structure
// Strongly-typed anonymous data structure.
var allData = new[] { // array of parts
new { Num = 1, Details = new[] { // each part is keyed by object num
new {KeyChar = 'a', StringValue = "10"} , // key/value pair details
new {KeyChar = 'b', StringValue = "xyz"} ,
new {KeyChar = 'c', StringValue = "40"} }
},
new { Num = 12, Details = new[] {
new {KeyChar = 'a', StringValue = "20"} ,
new {KeyChar = 'b', StringValue = "os"} }
},
new { Num = 8, Details = new[] {
new {KeyChar = 'n', StringValue = "etc..."} }
}
};
The Types are automatically inferred by your consistent data declarations and generated into IL by the C# 3.x+ compiler.
Sample Usage
iterating over your data structure and printing it ....
foreach (var part in allData) {
Console.WriteLine("Object #" + part.Num + " contains the details: ");
foreach (var detail in part.Details)
Console.WriteLine(" - key: " + detail.KeyChar + ", value: " + detail.StringValue);
}
Stipulations
var, for implicitly typed variables, cannot be used at the class scope (i.e. to make fields) - it is restricted to method scope (i.e. as local variables).
There are some things to watch out for when using anonymous types, for example: Can't return anonymous type from method? Really?
The MSDN documentation describes some additional behaviour and "Gotchas".
- Anonymous instances are read-only, so you will need a different way to store and persist modifications. This may render it useless for your requirements.
However, it was fun to include this answer as an option because I learned something new today if nothing else. :)
Edit/Update: Writable version
(modification to make an equivalent writable data structure)
An equivalent writable version of the above data structure is the following, using System.Collections.Generic;
:
// Initialization (present data is read/writable)
Dictionary<int, List<Detail>> manageableData = new Dictionary<int, List<Detail>>()
{
{1, new List<Detail>() {
new Detail {KeyChar = 'a', StringValue="10"},
new Detail {KeyChar = 'b', StringValue="xyz"},
new Detail {KeyChar = 'c', StringValue="40"}
} },
{12, new List<Detail>() {
new Detail {KeyChar = 'a', StringValue="20"},
new Detail {KeyChar = 'b', StringValue="os"}
} }
};
// Can continue populating after initialization. E.g...
manageableData.Add(8, new List<Detail>() {
new Detail {KeyChar = 'n', StringValue="etc..."},
new Detail {KeyChar = 'z', StringValue="etc..."}
});
A small helper class is declared to make initialization of detail data more readable; the Detail
helper class replaces what could simply be KeyValuePair<char, string>
. According to taste.
public class Detail {
public char KeyChar { get; set; }
public string StringValue { get; set; }
}
... effectively allows us to use new Detail {KeyChar = 'b', StringValue="xyz"}
for init of detail items instead of new KeyValuePair<char, string>('b', "xyz")
.
Sample Usage
iterating over your data structure and printing it ....
foreach (var part in manageableData) {
Console.WriteLine("Object #" + part.Key + " contains the details: ");
foreach (var detail in part.Value)
Console.WriteLine(" - key: " + detail.KeyChar + ", value: " + detail.StringValue);
}
Another variation on Writable data structure (less abstract)
(no unneeded abstraction - just raw collections)
Without the custom Detail class, you'd nest your dictionaries like
Dictionary<int, Dictionary<char, string>> data2 = new Dictionary<int, Dictionary<char, string>>()
{
{1, new Dictionary<char, string>() {
{'a', "10"},
{'b', "xyz"},
{'c', "40"}
} }
};
data2.Add(8, new Dictionary<char,string>() {
{'n', "etc..."},
{'z', "etc..."}
});
// SAMPLE USAGE:
// Once again, very minor changes to the mechanism of accessing the data structure:
foreach (var part in data2) {
Console.WriteLine("Object #" + part.Key + " contains the details: ");
foreach (var detail in part.Value)
Console.WriteLine(" - key: " + detail.Key + ", value: " + detail.Value);
}
Name "Aliasing" for readability
This is the plain nested dictionary scenario to store file objects and attributes.
// initialize
Dictionary<int, Dictionary<char, string>> data1 = new Dictionary<int, Dictionary<char, string>>()
{
{1, new Dictionary<char, string>() {
{'a', "10"},
{'b', "xyz"},
{'c', "40"}
}}
};
// populate
data1.Add(8, new Dictionary<char, string>() {
{'n', "etc..."},
{'z', "etc..."}
});
Making a more Descriptive/Readable Version
There are ways to make nested data structures more readable. Here's one sample to show some readability differences. Likely this isn't the smartest way because it adds a couple of Types just for the sake of aliasing but nonetheless...
This is the exact same data structure as above but using "aliased" names:
// initialize
FileObjects data2 = new FileObjects()
{
{1, new ObjectAttributes() {
{'a', "10"},
{'b', "xyz"},
{'c', "40"}
}}
};
// populate
data2.Add(8, new ObjectAttributes() {
{'n', "etc..."},
{'z', "etc..."}
});
The following "alias" definitions effectively rename the original Generics (through inheritance) to more descriptive types and hide the Type Parameters.
public class ObjectAttributes : Dictionary<char, string> { }
public class FileObjects : Dictionary<int, ObjectAttributes> { }
Likely you'd need more nested data before this type of approach becomes viable.