C# - Adding objects dynamically (adding dynamic property names) - c#

I'm trying to create some dynamic ExpandoObject. I've encountered a certain problem.
As I don't know what the name of these different properties in my objects should be, I can't do like this:
var list = new ArrayList();
var obj = new ExpandoObject();
obj.ID = 1,
obj.Product = "Pie",
obj.Days = 1,
obj.QTY = 65
list.Add(obj);
Let me explain my situation: I wish to get data from a random DB (I don't know which, but building a connection string from the information I get from the UI), therefore I don't know what data I need to get. This could be an example of a DB table
TABLE Sale
ID: int,
Product: nvarchar(100),
Days: int,
QTY: bigint
This could be another exmaple:
TABLE Foobar
Id: int,
Days: int
QTY: bigint
Product_Id: int
Department_Id: int
As you see, I don't know what the DB looks like (this is 100% anonymous, therefore it needs to be 100% dynamic), and the data I want to return should look like a well constructed JSON, like so:
[
{
"ID": 1,
"Product": "Pie"
"Days": 1,
"QTY": 65
},
{
"ID": 2,
"Product": "Melons"
"Days": 5,
"QTY": 12
}
]
Or, with the other example:
[
{
"ID": 1,
"Days": 2,
"QTY": 56,
"Product_Id": 5,
"Department_Id": 2
}
{
"ID": 2,
"Days": 6,
"QTY": 12,
"Product_Id": 2,
"Department_Id": 5
}
]
I've tried working with these ExpandoObjects, but can't seem to make it work, as I can't do what's illustrated in the top of this question (I don't know the names of the properties). Is there a way for me to say something like:
var obj = new ExpandoObject();
var propName = "Product";
var obj.propName = "Pie"
Console.WriteLine("Let's print!: " + obj.Product);
//OUTPUT
Let's print!: Pie
Does anyone have a solution, og simply guidance to a structure, that might solve this situation?

Rather than creating an ExpandoObject or some other dynamic type, you could create a List<Dictionary<string, object>> where each Dictionary<string, object> contains the name/value pairs you want to serialize. Then serialize to JSON using Json.NET (or JavaScriptSerializer, though that is less flexible):
var list = new List<Dictionary<string, object>>();
// Build a dictionary entry using a dictionary initializer: https://msdn.microsoft.com/en-us/library/bb531208.aspx
list.Add(new Dictionary<string, object> { { "ID", 1 }, {"Product", "Pie"}, {"Days", 1}, {"QTY", 65} });
// Build a dictionary entry incrementally
// See https://msdn.microsoft.com/en-us/library/xfhwa508%28v=vs.110%29.aspx
var dict = new Dictionary<string, object>();
dict["ID"] = 2;
dict["Product"] = "Melons";
dict["Days"] = 5;
dict["QTY"] = 12;
list.Add(dict);
Console.WriteLine(JsonConvert.SerializeObject(list, Formatting.Indented));
Console.WriteLine(new JavaScriptSerializer().Serialize(list));
The first outputs:
[
{
"ID": 1,
"Product": "Pie",
"Days": 1,
"QTY": 65
},
{
"ID": 2,
"Product": "Melons",
"Days": 5,
"QTY": 12
}
]
The second outputs the same without the indentation:
[{"ID":1,"Product":"Pie","Days":1,"QTY":65},{"ID":2,"Product":"Melons","Days":5,"QTY":12}]

Use dynamic, then cast to IDictionary<string, object> to loop through your properties:
dynamic obj = new ExpandoObject();
obj.Product = "Pie";
obj.Quantity = 2;
// Loop through all added properties
foreach(var prop in (IDictionary<string, object>)obj)
{
Console.WriteLine(prop.Key + " : " + prop.Value);
}
I've made a fiddle: https://dotnetfiddle.net/yFLy2u
Now this is a solution to your question... other answers like #dbc's might be better suited to the problem (which is not the question, really)

As you can see here ExpandoObject Class, the ExpandoObject is implementing IDictionary<string, object>, so you can use that fact like
IDictionary<string, object> obj = new ExpandoObject();
var propName = "Product";
obj[propName] = "Pie"
Console.WriteLine("Let's print!: " + obj[propName]);
// Verify it's working
Console.WriteLine("Let's print again!: " + ((dynamic)obj).Product);

While I was writing the answer, I see you already got proper answer. You can use a Dictionary<string, onject> or even Tuple.
But as per your original question, you wanted to add properties dynamically. For that you can refer to other answer using ExpandoObject. This is just the same solution (using ExpandoObject to dynamically add properties) with classes similar to your code.
//example classes
public class DictKey
{
public string DisplayName { get; set; }
public DictKey(string name) { DisplayName = name; }
}
public class DictValue
{
public int ColumnIndex { get; set; }
public DictValue(int idx) { ColumnIndex = idx; }
}
//utility method
public static IDictionary<string, object> GetExpando(KeyValuePair<DictKey, List<DictValue>> dictPair)
{
IDictionary<string, object> dynamicObject = new ExpandoObject();
dynamicObject["Station"] = dictPair.Key.DisplayName;
foreach (var item in dictPair.Value)
{
dynamicObject["Month" + (item.ColumnIndex + 1)] = item;
}
return dynamicObject;
}
Ans usage example:
var dictionaryByMonth = new Dictionary<DictKey, List<DictValue>>();
dictionaryByMonth.Add(new DictKey("Set1"), new List<DictValue> { new DictValue(0), new DictValue(2), new DictValue(4), new DictValue(6), new DictValue(8) });
dictionaryByMonth.Add(new DictKey("Set2"), new List<DictValue> { new DictValue(1), new DictValue(2), new DictValue(5), new DictValue(6), new DictValue(11) });
var rowsByMonth = dictionaryByMonth.Select(item => GetExpando(item));

First part, read this blog post by C# team thoroughly.
Lets see your code
var obj = new ExpandoObject();
var propName = "Product";
var obj.propName = "Pie"
Console.WriteLine("Let's print!: " + obj.Product);
//OUTPUT
Let's print!: Pie
In your code you are using var obj = new ExpandoObject();, so you are creating a statically typed object of type ExpandableObject. In the blog they specifically call out
I didn’t write ExpandoObject contact = new ExpandoObject(), because if I did contact would be a statically-typed object of the ExpandoObject type. And of course, statically-typed variables cannot add members at run time. So I used the new dynamic keyword instead of a type declaration, and since ExpandoObject supports dynamic operations, the code works
So if you rewrite your code to use dynamic obj, and add the dynamic properties as properties it should work!
But for your particular use case you better use Dictionaries as suggested above by #dbc
dynamic obj = new ExpandoObject();
obj.Product= "Pie"
Console.WriteLine("Let's print!: " + obj.Product);
//OUTPUT
Let's print!: Pie

Related

How to easily merge two anonymous objects with different data structure?

I would like to merge these two anonymous objects:
var man1 = new {
name = new {
first = "viet"
},
age = 20
};
var man2 = new {
name = new {
last = "vo"
},
address = "123 street"
};
Into a single one:
var man = new {
name = new {
first = "viet",
last = "vo"
},
age = 20,
address = "123 street"
};
I looked for a solution but found nothing clever.
Convert the anonymous object to ExpandoObject which is essentially a dictionary of string key and object value:
var man1Expando = man1.ToDynamic();
var man2Expando = man2.ToDynamic();
public static ExpandoObject ToDynamic(this object obj)
{
IDictionary<string, object> expando = new ExpandoObject();
foreach (var propertyInfo in obj.GetType().GetProperties())
{
var currentValue = propertyInfo.GetValue(obj);
if (propertyInfo.PropertyType.IsAnonymous())
{
expando.Add(propertyInfo.Name, currentValue.ToDynamic());
}
else
{
expando.Add(propertyInfo.Name, currentValue);
}
}
return expando as ExpandoObject;
}
I'm using a helper extension to establish whether a type is an anonymous one:
public static bool IsAnonymous(this Type type)
{
return type.DeclaringType is null
&& type.IsGenericType
&& type.IsSealed
&& type.IsClass
&& type.Name.Contains("Anonymous");
}
Then, merge two resulting expando objects into one, but recursively, checking for nested expando objects:
var result = MergeDictionaries(man1Expando, man2Expando, overwriteTarget: true);
public static IDictionary<string, object> MergeDictionaries(
IDictionary<string, object> targetDictionary,
IDictionary<string, object> sourceDictionary,
bool overwriteTarget)
{
foreach (var pair in sourceDictionary)
{
if (!targetDictionary.ContainsKey(pair.Key))
{
targetDictionary.Add(pair.Key, sourceDictionary[pair.Key]);
}
else
{
if (targetDictionary[pair.Key] is IDictionary<string, object> innerTargetDictionary)
{
if (pair.Value is IDictionary<string, object> innerSourceDictionary)
{
targetDictionary[pair.Key] = MergeDictionaries(
innerTargetDictionary,
innerSourceDictionary,
overwriteTarget);
}
else
{
// What to do when target propety is nested, but source is not?
// Who takes precedence? Target nested property or source value?
if (overwriteTarget)
{
// Replace target dictionary with source value.
targetDictionary[pair.Key] = pair.Value;
}
}
}
else
{
if (pair.Value is IDictionary<string, object> innerSourceDictionary)
{
// What to do when target propety is not nested, but source is?
// Who takes precedence? Target value or source nested value?
if (overwriteTarget)
{
// Replace target value with source dictionary.
targetDictionary[pair.Key] = innerSourceDictionary;
}
}
else
{
// Both target and source are not nested.
// Who takes precedence? Target value or source value?
if (overwriteTarget)
{
// Replace target value with source value.
targetDictionary[pair.Key] = pair.Value;
}
}
}
}
}
return targetDictionary;
}
The overwriteTarget parameter decides which object takes priority when merging.
Usage code:
var man1 = new
{
name = new
{
first = "viet",
},
age = 20,
};
var man2 = new
{
name = new
{
last = "vo",
},
address = "123 street",
};
var man1Expando = man1.ToDynamic();
var man2Expando = man2.ToDynamic();
dynamic result = MergeDictionaries(man1Expando, man2Expando, overwriteTarget: true);
Console.WriteLine(JsonConvert.SerializeObject(result, Formatting.Indented));
and the result:
{
"name": {
"first": "viet",
"last": "vo"
},
"age": 20,
"address": "123 street"
}
Notice how I assigned the result to dynamic. Leaving compiler assign the type will leave you with expando object presented as IDictionary<string, object>. With a dictionary representation, you cannot access properties in the same manner as if it was an anonymous object:
var result = MergeDictionaries(man1Expando, man2Expando, overwriteTarget: true);
result.name; // ERROR
That's why the dynamic. With dynamic you are losing compile time checking, but have two anonymous objects merged into one. You have to judge for yourself if it suits you.
There's nothing built-in in the C# language to support your use case. Thus, the question in your title needs to be answered with "Sorry, there is no easy way".
I can offer the following alternatives:
Do it manually:
var man = new {
name = new {
first = man1.name.first,
last = man2.name.first
},
age = man1.age,
address = man2.address
};
Use a class instead of an anonymous type for the resulting type (let's call it CompleteMan). Then, you can
create a new instance var man = new CompleteMan(); ,
use reflection to collect the properties and values from your "partial men" (man1 and man2),
assign those values to the properties of your man.
It's "easy" in the sense that the implementation will be fairly straight-forward, but it will still be a lot of code, and you need to take your nested types (name) into account.
If you desperately want to avoid non-anonymous types, you could probably use an empty anonymous target object, but creating this object (var man = new { name = new { first = (string)null, last = (string)null, ...) is not really less work than creating a class in the first place.
Use a dedicated dynamic data structure instead of anonymous C# classes:
The Newtonsoft JSON library supports merging of JSON objects.
Dictionaries can also be merged easily.
ExpandoObjects can be merged easily as well.

Why this confusing syntax exists?

I've just read this question.
If we have property of dictionary type:
public class Test
{
public Dictionary<string, string> Dictionary { get; set; } = new Dictionary<string, string>
{
{"1", "1" },
{"2", "2" },
};
}
Then we can construct object and add value to it
var test = new Test { Dictionary = { { "3", "3" } } };
Console.WriteLine(test.Dictionary.Count); // 3
And I don't understand the point why such a confusing syntax to add items exists? When looking at someone else code it's very easy to confuse it with very similarly looking
var test = new Test { Dictionary = new Dictionary<string, string> { { "3", "3" } } };
Console.WriteLine(test.Dictionary.Count); // 1
I'd be more OK with it if following would be possible (but it's not):
var dictionary = new Dictionary<string, string> { { "1", "1" } };
...
// adding a new value
dictionary = { { "2" , "2"} }; // invalid expression term '{'
So why this form of adding was needed and exists? For interviews?
The collection initializer syntax is simply a convenient way of initializing collections (including dictionaries) as part of a complex object model using an object initializer. For example:
var model = new SomeModel {
Name = "abc",
Id = 42,
SpecialMaps = {
{ "foo", "bar" },
{ "magic", "science" },
}
};
If you don't like it: just don't use it; but the equivalent with manual .Add is IMO much less elegant - a lot of things are taken care of automatically, such as only reading the property once. The longer version that actually creates the collection at the same time works very similarly.
Note that there is also an indexer variant now:
var model = new SomeModel {
Name = "abc",
Id = 42,
SpecialMaps = {
["foo"] = "bar",
["magic"] ="science",
}
};
This is very similar, but instead of using collection.Add(args); it uses collection[key] = value;. Again, if it confuses you or offends you: don't use it.
Take this example where the constructor of Thing creates a Stuff and the constructor of Stuff creates the Foo list
var thing = new Thing();
thing.Stuff.Foo.Add(1);
thing.Stuff.Foo.Add(2);
thing.Stuff.Foo.Add(3);
And now you can simplify it to the following with initializers.
var thing = new Thing
{
Stuff.Foo = { 1, 2, 3 }
};
You can only use this type of initialization for a collection without first newing up the collection when nested because the collection can exist in this case, but cannot when assigning directly to a variable.
Ultimately this type of syntactic sugar is likely added by the language designers when they see code patterns that they think can be simplified.

C# REST dictionary DTO

What is the best way to design a DTO for a REST API to get a resource that is semantically a dictionary?
Example: Get the number of employees by age -> should return a set of couples (age, number) of type (int, int).
An easy way should be an array of structures, like:
[
{
age: 30,
number: 3
},
{
age: 31,
number: 4
},
{
age: 32,
number: 5
}
]
but this does not enforces that the key should be unique.
Semantically, I'd rather prefer a dictionary, like:
{
"30": 3,
"31": 4,
"32": 5
}
Is there a way to define such a DTO in C#, so that it can be easily serialized/deserialized?
Your second Json is not valid, it should look like:
{
"30": 3,
"31": 4,
"32": 5
}
And in c# it's just a Dictionary<string, int>
You could easiliy deserialize/serialize it with Json.Net
var result =JsonConvert.DeserializeObject<Dictionary<string, int>>(text);
var serialized = JsonConvert.SerializeObject(
new Dictionary<string, int> { { "1", 2 }, {"3",4}});
As suggested by maksim-simkin in his answer, dictionaries are natively supported by Json.Net.
Here's an example:
var result = JsonConvert.DeserializeObject<Dictionary<int, int>>(text);
var serialized = JsonConvert.SerializeObject(new Dictionary<int, int> { { 30, 3 }, { 31, 4}});
// here serialized == "{\"30\":3,\"31\":4,\"32\":5}"
If it can be useful, this serialization is supported in Nancy through the DynamicDictionary class (in namespace Nancy). In a NancyModule, you can create a model like this:
DynamicDictionary dynamicDictionary = ToDynamicDictionary(dictionary);
return this
.Negotiate
.WithModel(dynamicDictionary)
.WithStatusCode(HttpStatusCode.OK);
Then, you have to convert your keys to a string, using this helper method:
private static DynamicDictionary ToDynamicDictionary(IDictionary<int, int> dictionary)
{
DynamicDictionary result = new DynamicDictionary();
foreach (var pair in dictionary)
{
result.Add(pair.Key.ToString(CultureInfo.InvariantCulture), pair.Value);
}
return result;
}

Insert List<object> into Elasticsearch with NEST

I have a lot of different collections of values I generate at runtime and want to send them to ElasticSearch. I can represent them as List<object> or if really doesn't work any other way, as List<string>. But I can't find any example how to do that. Here is an example of the code which doesn't work. There is probably a lot wrong with it, so any additional pointers are highly appreciated.
var client = new ElasticClient(new Uri("http://localhost:9200"));
client.CreateIndex("testentry");
var values = new List<object> {"StringValue", 123, DateTime.Now};
var indexResponse = client.Index(values, descriptor => descriptor.Index("testentry"));
Console.WriteLine(indexResponse.DebugInformation);
Which results in:
Invalid NEST response built from a unsuccessful low level call on POST: /testentry/list%601
# Audit trail of this API call:
- [1] BadResponse: Node: http://localhost:9200/ Took: 00:00:00.0600035
# ServerError: ServerError: 400Type: mapper_parsing_exception Reason: "failed to parse" CausedBy: "Type: not_x_content_exception Reason: "Compressor detection can only be called on some xcontent bytes or compressed xcontent bytes""
and
[2016-09-17 14:16:20,955][DEBUG][action.index ] [Gin Genie] failed to execute [index {[t
estentry][list`1][AVc4E3HaPglqpoLcosDo], source[_na_]}] on [[testentry][1]]
MapperParsingException[failed to parse]; nested: NotXContentException[Compressor detection can only
be called on some xcontent bytes or compressed xcontent bytes];
at org.elasticsearch.index.mapper.DocumentParser.parseDocument(DocumentParser.java:156)
I'm using Elasticsearch.Net 2.4.3 and NEST 2.4.3.
In addition to Henrik's answer, you could also index values in a Dictionary<string, object>
public class MyType
{
public MyType()
{
Values = new Dictionary<string, object>();
}
public Dictionary<string, object> Values { get; private set; }
}
void Main()
{
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings = new ConnectionSettings(pool);
var client = new ElasticClient(connectionSettings);
var myType = new MyType
{
Values =
{
{ "value1", "StringValue" },
{ "value2", 123 },
{ "value3", DateTime.Now },
}
};
client.Index(myType, i => i.Index("index-name"));
}
The Dictionary<string,object> will be serialized to a json object with property names to match the dictionary keys
{
"values": {
"value1": "StringValue",
"value2": 123,
"value3": "2016-09-18T18:41:48.7344837+10:00"
}
}
Within Elasticsearch, the mapping will be inferred as an object type.
Arrays with a mixture of datatypes are not supported.
You could convert all of the values to strings:
client.CreateIndex("testentry");
var values = new List<string> { "StringValue", "123", DateTime.Now.ToString() };
var indexResponse = client.Index(new { Values = values}, descriptor => descriptor.Index("testentry").Type("test"));
Or specify the fields that the values should be indexed to:
client.CreateIndex("testentry");
var values = new { Field1 = "StringValue", Field2 = 123, Field3 = DateTime.Now };
var indexResponse = client.Index(values, descriptor => descriptor.Index("testentry").Type("test"));
Consider specifying the type of the document with the IndexDescriptor or create a class for the document.

C# storing data in data structure

I have data like..
1 -> a 10
b xyz
c 40
12 -> a 20
b os
8 -> ..............
how to store this data in data structure. which DS is suitable for it in C#.
1,12,8 are the object no. & a,b,c are the there attribute key & value pair.
it is internal file representation of .. file.
So i want to store it for further manipulation operations.
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.
a Dictionary<int,Dictionary<string,string>>
Edit:
if you only have 'a' 'b' 'c' as the keys, you'd just use string[] rec = new string[3] instead of a dictionary.
Data content itself is just one aspect of data structure choice. A more important guideline is how you will create, manipulate, and access the data.
List<Dictionary<char, object>> will handle ordering if you want to access 1, 2, 3, etc.. in an ordered fashion and allow the second level to be any type of content you want.
Dictionary<int, Dictionary<string, string>> would allow you to do fast lookups of any top level 1, 2, 3, etc... and would assume that the a / 10, b / xyz, etc... are always encoded as strings.
It would help if you told us how you were using this data.
Raw structure you could use:
Dictionary<int, Dictionary<char, object>> //or
Dictionary<int, Dictionary<string, object>> //the more common construct, or
Dictionary<int, Dictionary<string, string>> //would require casting on some objects
This probably wont be optimal for your situation though, depending on how you intend to search/access this.
Depending on the meaning of your data a specific class implementation and a Dictionary implementation might work better.

Categories