This problem reminds me of the minigame Doodle God. There are several objects and some of them can interact with each other and form new objects. Each object is naturally its own class: water, fire, air, etc. These all inherit from the same base class. The water and fire objects, for example, could be combined to form an ash object which can be used in new combinations.
The problem is figuring out an elegant way to handle all the possible combinations. The most obvious, but horribly unmaintainable, solution would be creating a function that takes any two objects as parameters and uses a huge switch block to compare typenames and figure out what kind of object (if any) should be returned when these two interact. It is also important that combine(a, b) should always equal combine(b, a).
What would be a maintainable and efficient design for this scenario?
We had to take code for this in a game to collide items. We ended up going for a two dimensional structure that stored a bunch of delegate methods.
| air | wind | fire
air |combine(air,air)|combine(air,wind) |combine(air,fire)
wind | |combine(wind,wind)|combine(wind,fire)
fire | | |combine(fire,fire)
with a bit of thinking, you only need to populate just over half of the combining matrix.
You could (for instance):
lookup =
new Dictionary<
Tuple<Type, Type>,
Func<ICombinable, ICombinable, ICombinable>();
lookup.Add(
Tuple.Create(typeof(Air), typeof(Fire)),
(air,fire) => return new Explosion());
Then have a single method:
ICombinable Combine(ICombinable a,ICombinable b)
{
var typeA = a.GetType();
var typeB = b.GetType();
var typeCombo1 = Tuple.Create(typeA,typeB);
Func<ICombinable,ICombinable,ICombinable> combineFunc;
if(lookup.TryGetValue(typeCombo1, out combineFunc))
{
return combineFunc(a,b);
}
var typeCombo2 = Tuple.Create(typeB,typeA);
if(lookup.TryGetValue(typeCombo2, out combineFunc))
{
return combineFunc(b,a);
}
//throw?
}
All game objects are already designed in some way. They are either hardcoded or read at runtime from a resource.
This data structure can easily be stored in a Dictionary<Element, Dictionary<Element, Element>>.
var fire = new FireElement();
var water = new WaterElement();
var steam = new SteamElement();
_allElements = Dictionary<Element, Dictionary<Element,Element>>
{
new KeyValuePair<Element, Dictionary<Element, Element>>
{
Key = fire,
Value = new KeyValuePair<Element, Element>
{
Key = water,
Value = steam
}
},
new KeyValuePair<Element, Dictionary<Element, Element>>
{
Key = water,
Value = new KeyValuePair<Element, Element>
{
Key = fire,
Value = steam
}
}
}
When loading or defining the elements, you can just duplicate them, as there'll at most be a few hundred. The overhead is neglectable for the ease of coding IMO.
The keys of _allElements contain all existing, combinable elements. The value of _allElements[SomeElement] yields yet another dictionary, which you can access on the elment you wish to combine it with.
This means you can find the resulting element of a combination with the following code:
public Element Combine(Element element1, Element element2)
{
return _allElements[element1][element2];
}
Which, when called as such:
var resultingElement = Combine(fire, water);
Yields steam, the same result as were Combine(water, fire) called.
Untested, but I hope the principle applies.
Exactly this is the right place for interfaces. With them you can avoid the big switch and each element class can implement its own behaviour of interacting with another elemt class.
I would propose using an Abstract Factory returning a specific kind of interface, lets say InteractionOutcome. You would not escape the need of using a switch-case but you would end up with something much more maintenable using different factories for each "construction".
Hope I helped!
Related
I am trying to create a diff between protobuf messages that would work along all the IMessage objects (as far as I can tell the interface every protobuf implements) in the codebase.
To this end, I have made a generic method that would take any IMessage implementation (that provides a parameterless constructor) and would attempt to create a delta.
public class ProtocolDiffer<T> where T : IMessage, new()
{
public T Diff(T original, T replacement)
{
var delta = new T();
foreach (var fieldDescriptor in replacement.Descriptor.Fields.InFieldNumberOrder())
{
// Obtain the pair of values to compare
var originalFieldValue = fieldDescriptor.Accessor.GetValue(original);
var replacementFieldValue = fieldDescriptor.Accessor.GetValue(replacement);
Console.WriteLine(
$"Is the field {fieldDescriptor.Name.ToUpper()} equal between Original and Replacement? " +
$"{originalFieldValue.Equals(replacementFieldValue)}");
// These fields are equal, jump to the next pair
if (originalFieldValue.Equals(replacementFieldValue)) continue;
// They were not equal
// Is it a simple field?
if (fieldDescriptor.FieldType == FieldType.Message)
{
// Non-basic fields need to be evaluated instead of simply replaced
fieldDescriptor.Accessor.SetValue(delta,
Diff((T)(IMessage) fieldDescriptor.Accessor.GetValue(original),
(T)(IMessage) fieldDescriptor.Accessor.GetValue(replacement)));
}
else
{
fieldDescriptor.Accessor.SetValue(delta, fieldDescriptor.Accessor.GetValue(replacement));
}
}
return delta;
}
...
}
The issue I am having is that the recursive mechanism fails since eventually the object being treated changes from the class that were generated through protoc to a RepeatedField and the Diff will not be able to cast the objects.
I have tried using IMessage as the parameter to instantiate this Diff class but it does not provide a parameterless constructor. I am now considering creating a class that implements IMessage that would be able to be cast to by the protoc generated classes and RepeatedField but that will still take some time and I am unsure if I would even be able to provide the necessary casting in the first place.
Finally, this is all being done so that I can return the difference between two structures, including primitive fields and complex ones such as Lists, the idea being that the recursive mechanism would traverse the whole structure until it replaces every different field and then returns the structure with only those same fields set.
Is there a way to fix the casting issue so I can keep using the recursive mechanism?
I suspect you will need to simply deal with repeated fields (and maps) as separate branches in your generic diff method.
The IsRepeated attribute of a FieldDescriptor will tell you "in advance" whether a field is repeated or not. Likewise IsMap for maps. The GetValue documentation tells you what you can convert the returned objects to:
For repeated values, this will be an IList implementation. For map values, this will be an IDictionary implementation.
The exact details would depend on how you want to define what a diff between two repeated fields (or maps) is. For example, you might want to apply your recursive diff process to matching pairs (list indices, or map keys), but it's not entirely obvious what the delta result should contain for elements that have been removed in the replacement (nothing?), or added in it (a copy of the entire added message?). For a repeated field, you might also need to add placeholder items (default instances?) even for equal elements, otherwise the caller would not know which list indices contained the non-equal ones.
Finally, I don't think the recursive step in your example is correct. It assumes the submessage is also of type T, which isn't particularly likely. If the field has some other message type, the recursive call would need to return a delta of that type, not a T.
One possibility would be to write a method that's purely based on reflection, with the following general shape (not a complete example):
public IMessage Diff(IMessage original, IMessage replacement, out bool changed) {
// new, empty message of the same type; not sure if there's a cleaner way
IMessage delta = replacement.Descriptor.Parser.ParseFrom(ByteString.Empty);
changed = false;
foreach (var fieldDescriptor in replacement.Descriptor.Fields.InFieldNumberOrder()) {
if (fieldDescriptor.IsMap) {
var originalMap = (IDictionary<object, IMessage>) fieldDescriptor.Accessor.GetValue(original);
var replacementMap = (IDictionary<object, IMessage>) fieldDescriptor.Accessor.GetValue(replacement);
// ...your desired logic for comparing two maps...
} else if (fieldDescriptor.IsRepeated) {
var originalList = (IList<IMessage> fieldDescriptor.Accessor.GetValue(original);
var replacementList = (IList<IMessage> fieldDescriptor.Accessor.GetValue(replacement);
// ...your desired logic for comparing two lists...
} else if (fieldDescriptor.FieldType == FieldType.Message) {
var originalMessage = (IMessage) fieldDescriptor.Accessor.GetValue(original);
var replacementMessage = (IMessage) fieldDescriptor.Accessor.GetValue(replacement);
bool fieldChanged;
var fieldDelta = Diff(originalMessage, replacementMessage, out fieldChanged);
if (fieldChanged) {
changed = true;
fieldDescriptor.Accessor.SetValue(delta, fieldDelta);
}
} else {
var originalValue = fieldDescriptor.Accessor.GetValue(original);
var replacementValue = fieldDescriptor.Accessor.GetValue(replacement);
if (!originalValue.Equals(replacementValue)) {
changed = true;
fieldDescriptor.Accessor.SetValue(delta, replacementValue);
}
}
}
return delta;
}
For slightly better type safety, you could wrap that inside your proposed ProtocolDiffer<T> class to ensure that two messages of the same type are always passed in, and to indicate that the output will also be of the same type.
You might also drop the out bool changed out parameter and use the Equals method before the recursive step to determine if a field needs to be included in the outgoing delta message, like you're doing in your example. But that does a little more work than is necessary, because it involves performing a deep equality check on each "level" of the recursion.
I'm writing code in a C# library to do clustering on a (two-dimensional) dataset - essentially breaking the data up into groups or clusters. To be useful, the library needs to take in "generic" or "custom" data, cluster it, and return the clustered data.
To do this, I need to assume that each datum in the dataset being passed in has a 2D vector associated with it (in my case Lat, Lng - I'm working with co-ordinates).
My first thought was to use generic types, and pass in two lists, one list of the generic data (i.e. List<T>) and another of the same length specifying the 2D vectors (i.e. List<Coordinate>, where Coordinate is my class for specifying a lat, lng pair), where the lists correspond to each other by index. But this is quite tedious because it means that in the algorithm I have to keep track of these indices somehow.
My next thought was to use inferfaces, where I define an interface
public interface IPoint
{
double Lat { get; set; }
double Lng { get; set; }
}
and ensure that the data that I pass in implements this interface (i.e. I can assume that each datum passed in has a Lat and a Lng).
But this isn't really working out for me either. I'm using my C# library to cluster stops in a transit network (in a different project). The class is called Stop, and this class is also from an external library, so I can't implement the interface for that class.
What I did then was inherit from Stop, creating a class called ClusterableStopwhich looks like this:
public class ClusterableStop : GTFS.Entities.Stop, IPoint
{
public ClusterableStop(Stop stop)
{
Id = stop.Id;
Code = stop.Code;
Name = stop.Name;
Description = stop.Description;
Latitude = stop.Latitude;
Longitude = stop.Longitude;
Zone = stop.Zone;
Url = stop.Url;
LocationType = stop.LocationType;
ParentStation = stop.ParentStation;
Timezone = stop.Timezone;
WheelchairBoarding = stop.WheelchairBoarding;
}
public double Lat
{
get
{
return this.Latitude;
}
}
public double Lng
{
get
{
return this.Longitude;
}
}
}
which as you can see implements the IPoint interface. Now I use the constructor for ClusterableStop to first convert all Stops in the dataset to ClusterableStops, then run the algorithm and get the result as ClusterableStops.
This isn't really what I want, because I want to do things to the Stops based on what cluster they fall in. I can't do that because I've actually instantiated new stops, namely ClusterableStops !!
I can still acheive what I want to, because e.g. I can retrieve the original objects by Id. But surely there is a much more elegant way to accomplish all of this? Is this the right way to be using interfaces? It seemed like such a simple idea - passing in and getting back custom data - but turned out to be so complicated.
Since all you need is to associate a (latitude, longitude) pair to each element of 2D array, you could make a method that takes a delegate, which produces an associated position for each datum, like this:
ClusterList Cluster<T>(IList<T> data, Func<int,Coordinate> getCoordinate) {
for (int i = 0 ; i != data.Count ; i++) {
T item = data[i];
Coordinate coord = getCoord(i);
...
}
}
It is now up to the caller to decide how Coordinate is paired with each element of data.
Note that the association by list position is not the only option available to you. Another option is to pass a delegate that takes the item, and returns its coordinate:
ClusterList Cluster<T>(IEnumerable<T> data, Func<T,Coordinate> getCoordinate) {
foreach (var item in data) {
Coordinate coord = getCoord(item);
...
}
}
Although this approach is better than the index-based one, in cases when the coordinates are not available on the object itself, it requires the caller to keep some sort of an associative container on T, which must either play well with hash-based containers, or be an IComparable<T>. The first approach places no restrictions on T.
In your case, the second approach is preferable:
var clustered = Cluster(
myListOfStops
, stop => new Coordinate(stop.Latitude, stop.Longitude)
);
Have you considered using Tuples to do the work - sometimes this is a useful way of associating two classes without creating a whole new class. You can create a list of tuples:
List<Tuple<Point, Stop>>
where Point is the thing you cluster on.
In my scenario I have a class called Person. I need to test if certain people are compatible or not and return a bool value. I was thinking of using an enum setup to make it easier to test these compatibility tests. However I'm not familiar with enum and was hoping someone could shed some light or help demonstrate how i would use it in my case.
I was thinking it would be easiest to assign an id to each Person and a compatibility list along with that ID. Below is some pseudo code demonstrating what i mean. I'm just not clear on how to to set this up using enums.
ID's assigned to each class object
1 = Person(John)
2 = Person(Kevin)
3 = Person(Michelle)
4 = Person(Krystal)
5 = Person(Leslie)
Compatibility lists
1 = [2,4]
2 = [1,3,5]
3 = [2,5]
4 = [1]
5 = [2,3]
The tests I want to Perform and return a bool value.
If (Person(John) compatible with Person(Krystal))
{return true}else{return false}
Honestly, an enum is not the solution for this. The closest analogy to your "compatibility checker" would probably be an EqualityComparer<T> in .NET. It's a separate class.
The comparison "are two people compatible" really doesn't belong in the Person class. It depends on what measure of compatibility you are comparing them and over time that comparison may change or you may add other compatibility comparers.
So, instead of an enum create a CompatibilityComparer class. For now this has one method .IsCompatible(Person a, Person b) and inside that method you can use a dictionary, database lookup, complex calculation based on weighted values from a and b, or whatever else you want.
private static readonly CompatibilityComparer comparer
= new CompatibilityComparer();
...
if (comparer.IsCompatible(john, krystal)) ...
See separation of concerns and single responsibility principle.
Ideally your comparer would also operate on an interface IPerson rather than the concrete class Person so you can test it more easily with mock IPerson objects.
A simplest example, using a Dictionary of compatible people might be:
Dictionary<int, int[]> matrix = new Dictionary<int, int[]>();
// You could initialize this statically, or better yet, use Lazy<>
static CompatibilityComparer()
{
matrix[1] = new[] { 2, 4 };
...
}
public bool IsCompatible(Person a, Person b)
{
return matrix[a.Id].Contains(b.Id);
}
You could also represent your graph of compatibility as a list of pairs of compatible people ids, as a 2D square matrix, or any other graph representation.
If you really do have all the Person objects in memory, statically defined, it would be better to have a Dictionary<Person, List<Person>> although at some point one has to ask, "what's the real environment here?", it's not an interesting problem until there are thousands of People and they are in a database and then a different approach is needed again.
How was 'compatibility' decided? a) by a person entering data in a database or b) by some algorithm? If the former then that would involve Ids and a 'compatibility' table in the database with two foreign keys back to the people table (like the dictionary is meant to illustrate). And if the latter why isn't that in code?
I would suggest you to use enums together with extension methods. Let me explain how this would work for you.
public enum Person
{
John = 1,
Kevin = 2,
Michelle = 3,
Krystal = 4,
Leslie = 5
}
Here you have identifiers with an associated number set explicitly. However, this number association is optional and can be elided.
public static class PersonExtensions
{
private Dictionary<Person,List<Person>> compatiblePersons = createCompatiblePersons();
private static Dictionary<Person,List<Person>> createCompatiblePersons()
{
var d = new Dictionary<Person,List<Person>>;
// put your compatibilities here
d[Person.John] = new List()
{
Person.Kevin,
Person.Krystal
};
return d;
}
public static List<Person> GetCompatiblePersons(this Person person)
{
return compatiblePersons(person);
}
public static bool IsCompatibleWith(this Person person, Person other)
{
return this.GetCompatiblePersons().Contains(other);
}
}
This static class allows to use extension methods on any Person instance, e.g. Person.John.IsCompatibleWith(Person.Michelle) will return false in this case. The association is made in the Dictionary declared above. This technique allows you to add "properties" to your enums like the ability to ask for compatibility or get the list of compatible persons. However, i would suggest to choose a class if it gets more complex than this.
The answer of #OwlSolo in contrast does the job but is somewhat limited, but if your requirements are just as described I would recommend just adding a convenience extension method, which hides the logical bit calculations and take the [Flags] approach.
Code written blindly, so no warranties for compilation errors
What you want is an enum type with the flags attribute:
[Flags]
enum MyCompatibilities
{
a = 1,
b = 2,
c = 4,
d = 8
}
With this you can assign a number of enum elements that apply.
MYCompatibility comp = MYCompatibility.a | MYCompatibility.b;
| is a logical OR and it means that your variable comp has the properties a as well as b
You can find out whether a certain compatibility is set via bit comparison:
if (comp & MYCompatibility.a= != 0)
or with the logic provided by the [Flags] attribute:
if (comp.HasFlag(MYCompatibility.a))
For the inner workings of this, google for bit flags.
Good afternoon all!
As a part of getting a better grip on some of the most aspects of object based programming, I've started to attempt something far larger than I have done in the past. Hereby I'm trying to learn about inheritance, code reuse, using classes far more extensively, and so on.
For this purpose I am trying to piece together all the parts required for a basic RPG/dungeon crawler.
I know this has been done a billion times before, but I find that actually trying to code something like it takes you through a lot more problems than you might think, which is a great way to learn (I think).
For now I have only loaded up a WPF application, since my interest is 95% on being able to piece together the working classes, routines, functions, etc. And not so much interested in how it will look. I am actually reading up on XNA, but since I am mostly trying to get a grip on the basic workings, I don't want to complicate those aspects with the graphical side of things just yet.
The problem I am now facing is that when I would a character to attack or defend, it should know from which other character it came, or to which one it should be pointed. I figured I could either use a GUID, or a manually appointed ID. But the problem is that I don't really know how I can implement such a thing.
The thing that I figured was that I could maybe add a reference to an array (Character[]), and have a SearchByID function loop through them to find the right one, and return it. Like so:
internal Character SearchByID(string _ID)
{
foreach(Character charToFind in Character[])
{
if(charToFind.ID == _ID)
return charToFind;
}
}
This of course has to be altered a bit due to the return at the moment, but just to give you an idea.
What I am stuck on is how to create the appropriate array outside of the "Character"-class? I can fill it up just fine, but how do I go about having it added above class level?
The way the "Character"-class is built up is that every new character instantiates from the Character class. The constructor then loads the appropriate values. But other than this, I see no possibility to initialize an array outside of this.
If it is preferable to post the entire code that I have, that will be no problem at all!
Thanks for any insights you may provide me with.
I think you can just use the Character-class and pass other Characters to it, for example:
public class Character
{
public string Name { get; private set; }
public int HitPoints { get; private set; }
public int Offense { get; private set; }
public int Defense { get; private set; }
public Character(string name, int hitPoints, int offense, int defense)
{
Name = name;
HitPoints = hitPoints;
Offense = offense;
Defense = defense;
}
public void Defend(Character source)
{
HitPoints = HitPoints - (source.Offense - Defense);
if (HitPoints <= 0)
{
Console.WriteLine("{0} died", Name);
}
}
public void Attack(Character target)
{
// Here you can call the other character's defend with this char as an attacker
target.Defend(this);
if (target.HitPoints <= 0)
{
Console.WriteLine("{0} killed {1}", Name, target.Name);
}
}
}
The thing with object oriented programming is that you have to start thinking in objects. Objects are like boxes when they're concrete. You can make new ones and give them some properties, like a name, height, width, hitpoints, whatever. You can also let these objects perform actions. Now a simple box won't do much itself, but a character can do various things, so it makes sense to put these actions in the Character-class.
Besides having Characters, you might have a Game-class which manages the game-state, characters, monsters, treasure chests etc...
Now this simple example may cause you to gain HitPoints when your defense is higher than the attacker's offense, but that's details, I'll leave the exact implementation up to you.
I guess you want a way to insert characters in an array when they are instantiated..
You can make a static array or list
So,your class in my opinion should be
class Character
{
static List<Character> characterList=new List<Character>();//all characters are here
public Character(string id,...)
{
//initialize your object
characterList.Add(this);//store them in the list as and when created
}
internal Character SearchByID(string _ID)
{
foreach(Character charToFind in characterList)
{
if(charToFind.ID == _ID)
return charToFind;
}
}
}
As you may be knowing static members are associated with the class not with the object.So,when you create a new character object it would be automatically added to the characterList
Unless you are dealing with seperate processes, e.g. client-server, you probably don't want to use "Id"s at all.
Whereever you are passing string _ID around, pass the actual Character instead. This saves you looking up in an array or whatever.
Post more code, and I can show you what I mean.
You could use a dictionary, instantiated in your controller class:
Dictionary<Guid, Character> _characterList = new Dictionary<Guid, Character>();
Initialise:
var someCharacter = new Character() { stats = something };
var otherCharacter = new Character() { stats = anotherThing };
var char1Id = Guid.NewGuid();
var char2Id = Guid.NewGuid();
_characterList.Add(char1Id, someCharacter);
_characterList.Add(char2Id, otherCharacter);
then, to access characters:
var charToFind = _characterList[char1Id];
or
var charToFind = _characterList.Single(c => c.Name = "Fred The Killer");
or whatever else...
Check out keyed collection
KeyedCollection
It is like a dictionary where the key is a property of class.
You will be able to reference a Character with
Characters[id]
Syntax
On your Character class overrite GetHashCode and Equals for performance.
If you use Int32 for the ID then you will get a perfect hash.
Very fast and O(1).
I'm trying to figure out the best way to represent some data. It basically follows the form Manufacturer.Product.Attribute = Value. Something like:
Acme.*.MinimumPrice = 100
Acme.ProductA.MinimumPrice = 50
Acme.ProductB.MinimumPrice = 60
Acme.ProductC.DefaultColor = Blue
So the minimum price across all Acme products is 100 except in the case of product A and B. I want to store this data in C# and have some function where GetValue("Acme.ProductC.MinimumPrice") returns 100 but GetValue("Acme.ProductA.MinimumPrice") return 50.
I'm not sure how to best represent the data. Is there a clean way to code this in C#?
Edit: I may not have been clear. This is configuration data that needs to be stored in a text file then parsed and stored in memory in some way so that it can be retrieved like the examples I gave.
Write the text file exactly like this:
Acme.*.MinimumPrice = 100
Acme.ProductA.MinimumPrice = 50
Acme.ProductB.MinimumPrice = 60
Acme.ProductC.DefaultColor = Blue
Parse it into a path/value pair sequence:
foreach (var pair in File.ReadAllLines(configFileName)
.Select(l => l.Split('='))
.Select(a => new { Path = a[0], Value = a[1] }))
{
// do something with each pair.Path and pair.Value
}
Now, there two possible interpretations of what you want to do. The string Acme.*.MinimumPrice could mean that for any lookup where there is no specific override, such as Acme.Toadstool.MinimumPrice, we return 100 - even though there is nothing referring to Toadstool anywhere in the file. Or it could mean that it should only return 100 if there are other specific mentions of Toadstool in the file.
If it's the former, you could store the whole lot in a flat dictionary, and at look up time keep trying different variants of the key until you find something that matches.
If it's the latter, you need to build a data structure of all the names that actually occur in the path structure, to avoid returning values for ones that don't actually exist. This seems more reliable to me.
So going with the latter option, Acme.*.MinimumPrice is really saying "add this MinimumPrice value to any product that doesn't have its own specifically defined value". This means that you can basically process the pairs at parse time to eliminate all the asterisks, expanding it out into the equivalent of a completed version of the config file:
Acme.ProductA.MinimumPrice = 50
Acme.ProductB.MinimumPrice = 60
Acme.ProductC.DefaultColor = Blue
Acme.ProductC.MinimumPrice = 100
The nice thing about this is that you only need a flat dictionary as the final representation and you can just use TryGetValue or [] to look things up. The result may be a lot bigger, but it all depends how big your config file is.
You could store the information more minimally, but I'd go with something simple that works to start with, and give it a very simple API so that you can re-implement it later if it really turns out to be necessary. You may find (depending on the application) that making the look-up process more complicated is worse over all.
I'm not entirely sure what you're asking but it sounds like you're saying either.
I need a function that will return a fixed value, 100, for every product ID except for two cases: ProductA and ProductB
In that case you don't even need a data structure. A simple comparison function will do
int GetValue(string key) {
if ( key == "Acme.ProductA.MinimumPrice" ) { return 50; }
else if (key == "Acme.ProductB.MinimumPrice") { return 60; }
else { return 100; }
}
Or you could have been asking
I need a function that will return a value if already defined or 100 if it's not
In that case I would use a Dictionary<string,int>. For example
class DataBucket {
private Dictionary<string,int> _priceMap = new Dictionary<string,int>();
public DataBucket() {
_priceMap["Acme.ProductA.MinimumPrice"] = 50;
_priceMap["Acme.ProductB.MinimumPrice"] = 60;
}
public int GetValue(string key) {
int price = 0;
if ( !_priceMap.TryGetValue(key, out price)) {
price = 100;
}
return price;
}
}
One of the ways - you can create nested dictionary: Dictionary<string, Dictionary<string, Dictionary<string, object>>>. In your code you should split "Acme.ProductA.MinimumPrice" by dots and get or set a value to the dictionary corresponding to the splitted chunks.
Another way is using Linq2Xml: you can create XDocument with Acme as root node, products as children of the root and and attributes you can actually store as attributes on products or as children nodes. I prefer the second solution, but it would be slower if you have thousands of products.
I would take an OOP approach to this. The way that you explain it is all your Products are represented by objects, which is good. This seems like a good use of polymorphism.
I would have all products have a ProductBase which has a virtual property that defaults
virtual MinimumPrice { get { return 100; } }
And then your specific products, such as ProductA will override functionality:
override MinimumPrice { get { return 50; } }