Reputation: 2624
So let's say I have an interface in my game, IItem. IItem represents an object with a render component, a weight, and an attached "process". The process may be attached by someone using some ability to affect the item. Maybe something like an "enchantment" in an RPG. Let's say I want that attached process to be able to modify the weight of the IItem. However, I only want a process which is a component of the IItem to be able to do so. Objects outside of the IItem need to be able to get the weight though.
Do I implement a Weight property, with just a getter in the interface? Then does the base class implement a "SetWeight" method, which is declared internal to the Item namespace? That still does not really guarantee that only a component owned by the particular IItem can affect it of course, but at least something in the Input namespace cannot directly change the weight of an item. In C I could do something similar to linked lists in the kernel which can get their container, this would give me a way to make sure the weight and the process had the same container object, however, I do not see an easy way to do this in C#. Furthermore, I think it kind of damages the component based design for a component to have to have a reference back to its parent.
This is a general problem I am running in to as I design my interfaces and classes.
Upvotes: 0
Views: 67
Reputation: 1310
Have you considered using a Dictionary, instead of properties? Something like this:
// Item base class
public Dictionary<string, object> Attributes { get; private set; }
public List<Process> Enchantments { get; private set; }
public virtual T Get<T>(string identifier)
{
var att = this.Attributes.FirstOrDefault(att => att.Key == identifier);
if (att == null) throw new MissingAttributeExeption(identifier); // Or perhaps just return default(T)
if ((att.Value is T) == false) throw new InvalidAttributeCastException(identifier, typeof(T));
var value = att.Value;
foreach (var ench in this.Enchantments)
{
ench.Modify(identifier, ref value);
}
return value as T; // Maybe you need to cast value to Object, and then to T, I can't remember.
}
// Process class
public string ValueToModify { get; set }
public virtual void Modify(string identifier, ref object value)
{
if (identifier != this.ValueToModify) return;
// In an inherited class, for example a Weightless-Enchantment: Halfs all weight
var castedVal = value as int
value = castedVal / 2;
// Now this one item weights 50% of normal weight, and the original value is still stored in Item's Attributes dictionary.
}
// Some random class
public void Update()
{
var totalWeight = 0;
foreach (var item in this.itemCollection)
{
int weight = item.Get<int>("Weight");
totalWeight += weight;
}
Console.WriteLine("The player weights: {0}", totalWeight);
}
Obviously this means you can't really hardcode attributes, but... Do you ACTUALLY want to do this, in the long run? I mean once you start adding Intelligence, Agility, Strength, Stamina, Endurance, etc.
I know this doesn't solve your posed question, but I should think it's a pretty decent alternative.
Upvotes: 1
Reputation: 1300
Well, I think I get what you're talking about.
To ensure that your process, e.g. IEnchantment
may take an IItem
as a dependency(keep it as a member), and have IItem
have Update/Set methods that take an IEnchantment
, and then you can check that, for example:
public void Update(IEnchantment enchantment)
{
if (enchantment.AttachedItem != this)
throw new Exception("Cannot Update Item!");
/*
* Execute Updating Logics
*/
}
Here's one library I tried designing for my own games, maybe it can help you think about something. It's far from perfect, it's barely anything at all, but I hope it can help in any way.
Upvotes: 1