I made a utility debug class in a C# game I'm working on to be able to monitor and watch values of properties. Goes like this:
public static class Monitor
{
private static List<object> monitoredObjects;
public static void Initialize()
{
monitoredObjects = new List<object>();
}
public static void Watch(object o)
{
monitoredObjects.Add(o);
}
public static void Unwatch(object o)
{
monitoredObjects.Remove(o);
}
public static void Draw(RenderWindow app)
{
//Not actual code, I actually draw this in game
foreach (object o in monitoredObjects)
Console.WriteLine(o.ToString());
}
}
public class Property
{
private object obj;
private PropertyInfo propertyInfo;
public override string ToString()
{
return propertyInfo.Name + ": " + propertyInfo.GetValue(obj, null).ToString();
}
public Property(object o, string property)
{
obj = o;
propertyInfo = o.GetType().GetProperty(property);
}
}
Now in order to monitor a property, say my game's FPS, I must do
Monitor.Watch(new Property(Game, "FPS"));
Wouldn't there be a way to somehow make this simpler to use? Ideally I'd like to be able to do
Monitor.Watch(Game.FPS);
But since we can't store pointers to value types in C#, I don't know how I would do this. Maybe using closures and lambada expressions? I was suggested this earlier but I'm not sure how to do it. Any other ways to improve this?
Thanks
Personally, what I would do is rework your Monitor class to accept a Func<string>
as input, and return a monitoring handle that could be used to "unmonitor" the class.
By doing that, you'd be able to write:
var handle = Monitor.Watch( () => Game.FPS.ToString() );
// later
Monitor.Unwatch(handle);
This could look something like:
public static class Monitor
{
private static Dictionary<IMonitorHandle, Func<string>> monitoredObjects;
public static void Initialize()
{
monitoredObjects = new Dictionary<IMonitorHandle, Func<string>>();
}
public static IMonitorHandle Watch(Func<string> o)
{
var handle = new MonitorHandle(o);
monitoredObjects.Add(handle, o);
return handle;
}
public static void Unwatch(IMonitorHandle handle)
{
monitoredObjects.Remove(handle);
}
public static void Draw(RenderWindow app)
{
//Not actual code, I actually draw this in game
foreach (object o in monitoredObjects.Values)
Console.WriteLine(o()); // Execute to get value...
}
}
You'd need to implement some interface for the handle - but this really could be anything, since it's just an object used as a hash table lookup for allowing unsubscription. You only need this to allow "Unwatch" to work, since you need to have some way to remove the delegate, which you'll probably want to define anonymously (as I did above).
Why are you not using INotifyPropertyChanged interface and just fire off the events within the Monitor class, something like this...assume your objects implement the interface...and every property in your objects raise a 'PropertyChanged' event with parameters indicating the values...in that way, it will be a fire and forget solution instead of looping through the list... as you call instantiate 'Monitor' with a 'RenderWindow' used as parameter to 'Initialize'. Also notice that 'Property' class is slightly modified to include a get accessor to return the object in question...
public static class Monitor
{
private static List monitoredObjects;
private static RenderWindow _app;
public static void Initialize(RenderWindow app)
{
monitoredObjects = new List();
}
public static void Watch(object o)
{
monitoredObjects.Add(o);
o.PropertyChanged += new EventHandler(monitor_PropertyChanged);
}
public static void Unwatch(object o)
{
o.PropertyChanged -= new EventHandler(monitor_PropertyChanged);
monitoredObjects.Remove(o);
}
public static monitor_PropertyChanged(object sender, PropertyChangedEventArgs e){
// Not actual code, I actually draw this in game
Console.WriteLine(e.SomeValue);
}
public static void Draw(RenderWindow app)
{
//Not actual code, I actually draw this in game
foreach (object o in monitoredObjects)
Console.WriteLine(o.ToString());
}
}
public class Property
{
private object obj;
private PropertyInfo propertyInfo;
public object PropObj{
get{ return this.obj; }
}
public override string ToString()
{
return propertyInfo.Name + ": " + propertyInfo.GetValue(obj, null).ToString();
}
public Property(object o, string property)
{
obj = o;
propertyInfo = o.GetType().GetProperty(property);
}
}
Hope this helps,
Best regards,
Tom.