1) Writing some code.

2) Launching from MonoDevelop:

3) Debugging!

4) Profit?
This should hopefully be available with the next MonoDevelop release :)
A cross platform open source .NET Framework based BitTorrent Client written in C#
void mono_profiler_install_gc_roots (MonoProfileGCHandleFunc handle_callback, MonoProfileGCRootFunc roots_callback);
struct _MonoProfiler {
const char *type_name;
GPtrArray *gchandles;
GPtrArray *stacktraces;
Moonlight::Mutex locker; /* used to ensure only one thread accesses the arrays */
static void track_gchandle (_MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj);
};
typedef _MonoProfiler MonoProfiler;
_MonoProfiler::_MonoProfiler ()
{
type_name = g_getenv ("GCHANDLES_FOR_TYPE");
gchandles = g_ptr_array_new ();
stacktraces = g_ptr_array_new_with_free_func (g_free);
/* Register the profiler with mono */
mono_profiler_install (this, NULL);
/* Supply a function for the gc_roots hook */
mono_profiler_install_gc_roots (track_gchandle, NULL);
/* Enable gc_roots tracking in the profiler so our hook is invoked */
mono_profiler_set_events (MONO_PROFILE_GC_ROOTS);
}
void
MonoProfiler::track_gchandle (MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj)
{
// Ignore anything that isn't a strong GC handle (docs say type == 2 is a strong gchandle)
if (type != 2)
return;
prof->locker.Lock ();
GPtrArray *gchandles = prof->gchandles;
GPtrArray *stacktraces = prof->stacktraces;
if (op == MONO_PROFILER_GC_HANDLE_CREATED) {
// Add the GCHandle to this array
g_ptr_array_add (gchandles, (gpointer) handle);
// If the target of the gchandle is of the correct type, store its stack trace
// Otherwise store NULL so that we can keep the index of the gchandle and corresponding
// stack trace in sync.
if (prof->type_name && !strcmp (prof->type_name, mono_class_get_name (mono_object_get_class(obj))))
g_ptr_array_add (stacktraces, get_stack_trace ());
else
g_ptr_array_add (stacktraces, NULL);
} else if (op == MONO_PROFILER_GC_HANDLE_DESTROYED) {
// Walk our list of gchandles and when we find the index of the destroyed handle
// remove the handle and corresponding stacktrace
for (int i = 0; i < (int)gchandles->len; i++) {
if (g_ptr_array_index (gchandles, i) == (gpointer) handle) {
g_ptr_array_remove_index_fast (gchandles, i);
g_ptr_array_remove_index_fast (stacktraces, i);
break;
}
}
}
prof->locker.Unlock ();
}
void
accumulate_g_ptr_array_by_type (gpointer data, gpointer user_data)
{
// The hashtable I passed in as the user_data in g_ptr_array_foreach
// which i am using to link a type name to a count
GHashTable *by_type = (GHashTable*) user_data;
// Get the MonoObject from the gchandle.
MonoObject *ob = mono_gchandle_get_target (GPOINTER_TO_INT (data));
// Get the type name from the mono_object
const char *name = mono_class_get_name (mono_object_get_class(ob));
// Find out how many instances we have already
int count = GPOINTER_TO_INT (g_hash_table_lookup (by_type, name)) + 1;
// Update the hashtable with an incremented count.
g_hash_table_insert (by_type, name, GINT_TO_POINTER (count));
}
1 instances GCHandled of type Surface
1 instances GCHandled of type Deployment
4 instances GCHandled of type NameScope
20 instances GCHandled of type ControlTemplate
218 instances GCHandled of type MonoType
3985 instances GCHandled of type Uri
struct NativeDate {
public int Day;
public int Month;
public int Year;
}
unsafe class Date {
IntPtr Native {
get; set;
}
public int Day {
get { return ((NativeDate *)Native)->Day; }
set { ((NativeDate *) Native)->Day = value; }
}
public int Month {
get { return ((NativeDate *) Native)->Month; }
set { ((NativeDate *) Native)->Month = value; }
}
public int Year {
get { return ((NativeDate *) Native)->Year; }
set { ((NativeDate *) Native)->Year = value; }
}
public Date (IntPtr native)
{
Native = native;
}
}

public class Secondary
{
public int First { get; set; }
public int Second { get; set; }
public int Third { get { return First + Second; } }
}
public class MyClass
{
public byte ByteProp { get; set; }
public short ShortProp { get; set; }
public int IntProp { get; set; }
public long LongProp { get; set; }
public string StringProp { get; set; }
}
static void Main(string[] args)
{
// Register a message so that all public fields will be serialized
Message.Register<MyClass>();
// Register a message so that only some fields are serialized and
// they are serialized in the specified order
Message.Register<Secondary>(
d => d.Second,
d => d.First
);
// Create a stream to serialize the data to
Stream s = new MemoryStream();
var message = new MyClass {
IntProp = 1,
LongProp= 2,
ByteProp= 3,
ShortProp = 4,
StringProp = "Hello World"
};
// Encode the message to the stream
MessageEncoder.Encode(message, s);
// Rewind the stream and then decode the message
s.Position = 0;
var decoded = MessageDecoder.Decode<MyClass>(s);
}
MessageEncoder.RegisterPrimitiveEncoder<int>((value, stream) => {
stream.Write(BitConverter.GetBytes(value));
});
public static class MessageEncoder
{
static Dictionary<Type, Delegate> encoders;
static Dictionary<Type, Delegate> primitives;
static MessageEncoder()
{
encoders = new Dictionary<Type, Delegate>();
primitives = new Dictionary<Type, Delegate>();
RegisterPrimitiveEncoders();
}
static void RegisterPrimitiveEncoders()
{
RegisterPrimitiveEncoder<byte>((value, stream) =>
stream.WriteByte(value)
);
RegisterPrimitiveEncoder<short>((value, stream) =>
stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value)))
);
RegisterPrimitiveEncoder<int>((value, stream) =>
stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value)))
);
RegisterPrimitiveEncoder<long>((value, stream) =>
stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value)))
);
var intWriter = (Action<int, Stream>)primitives[typeof (int)];
RegisterPrimitiveEncoder<string>((value, stream) => {
var buffer = Encoding.UTF8.GetBytes(value);
intWriter(buffer.Length, stream);
stream.Write(buffer);
});
}
public static void RegisterPrimitiveEncoder<T>(Action<T, Stream> encoder)
{
primitives [typeof (T)] = encoder;
}
public static void RegisterMessage<T>(params Expression<Func<T, object>>[] properties)
{
RegisterMessage<T>(properties.Select(p => p.AsPropertyInfo ()));
}
public static void RegisterMessage<T>(IEnumerable<PropertyInfo> properties)
{
var propertyEncoders = new List<Expression>();
// The encode function takes an instance of the class we're decoding and the Stream
// which we should write the data to.
ParameterExpression source = Expression.Parameter(typeof(T), "source_param");
ParameterExpression stream = Expression.Parameter(typeof(Stream), "stream");
// For each property, get the encoder which will convert the value of the property to a byte[]
// which can be written to the stream.
foreach (var property in properties) {
// Get the encoder for this property type
var action = primitives[property.PropertyType];
// Create a var which holds the Action <T, Stream> which encodes the data to the stream
Expression converter = Expression.Constant(action, action.GetType ());
// Invoke the encoder passing the value of the property and the 'stream'
Expression invoker = Expression.Invoke(converter, Expression.Property(source, property), stream);
// Add the encoder for this property to the list.
propertyEncoders.Add(invoker);
}
// Create an expression block which will execute each of the encoders one by one
Expression block = Expression.Block(propertyEncoders);
encoders.Add(typeof(T), Expression.Lambda<Action<T, Stream>>(
block,
source,
stream
).Compile());
}
public static void Encode<T>(T message, Stream s)
{
var encoder = (Action<T, Stream>)encoders[typeof (T)];
encoder (message, s);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;
using System.Net;
using System.IO;
namespace Encoder
{
public static class MessageDecoder
{
static Dictionary<Type, Delegate> decoders;
static Dictionary<Type, Delegate> primitives;
static MessageDecoder()
{
decoders = new Dictionary<Type, Delegate>();
primitives = new Dictionary<Type, Delegate>();
RegisterDefaultDecoders();
}
static void RegisterDefaultDecoders()
{
RegisterPrimitiveDecoder<byte>((s) => {
var val = s.ReadByte();
if (val == -1)
throw new EndOfStreamException();
return (byte)val;
});
RegisterPrimitiveDecoder<short>((s) => IPAddress.NetworkToHostOrder (s.ReadShort()));
RegisterPrimitiveDecoder<int>(s => IPAddress.NetworkToHostOrder (s.ReadInt()));
RegisterPrimitiveDecoder<long>(s => IPAddress.NetworkToHostOrder (s.ReadLong()));
var intDecoder = (Func<Stream, int>)primitives[typeof(int)];
RegisterPrimitiveDecoder<string>(s => {
var length = intDecoder(s);
var buffer = new byte[length];
s.Read(buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer);
});
}
public static void RegisterPrimitiveDecoder<T>(Func<Stream, T> decoder)
{
primitives.Add(typeof(T), decoder);
}
public static void RegisterMessage<T>(params Expression<Func<T, object>>[] properties)
{
RegisterMessage<T>(properties.Select(d => d.AsPropertyInfo()));
}
public static void RegisterMessage<T>(IEnumerable<PropertyInfo> properties)
{
var propertyDecoders = new List<Expression>();
// The decode function takes an instance of the class we're decoding and the Stream
// containing the data to decode.
ParameterExpression source = Expression.Parameter(typeof(T), "source_param");
ParameterExpression stream = Expression.Parameter(typeof(Stream), "stream");
// For each property, get the primitive decoder which will read data from the stream and
// return a value of the correct type.
foreach (var property in properties) {
var action = primitives[property.PropertyType];
// Create a var which holds the Func <Stream, T> which decodes the data from the stream
Expression decoder = Expression.Constant(action, action.GetType());
// Invoke the decoder passing 'stream' as the parameter
Expression invoker = Expression.Invoke(decoder, stream);
// Store the return value of the decoder in the property.
Expression setter = Expression.Call(source, property.GetSetMethod(), invoker);
// Add the decoder for this property to the list.
propertyDecoders.Add(setter);
}
// Create a block which will execute the decoders for all the fields one after another.
Expression block = Expression.Block(propertyDecoders);
decoders.Add (typeof (T), Expression.Lambda<Action<T, Stream>>(
block,
source,
stream
).Compile ());
}
public static T Decode<T>(Stream s) where T : class, new()
{
T t = new T();
var decoder = (Action<T, Stream>)decoders[typeof(T)];
decoder(t, s);
return t;
}
}
}
public Book()
{
// Boilerplate - eugh!
Action<string> notify = (propertyName) => {
var h = PropertyChanged;
if (h != null)
h(this, new PropertyChangedEventArgs(propertyName));
};
author = new ChangeNotifier<string> (() => Author, notify);
price = new ChangeNotifier<decimal> (() => Price, notify);
quantity = new ChangeNotifier<int> (() => Quantity, notify);
title = new ChangeNotifier<string> (() => Title, notify);
}
new ChangeNotifier<string>(() => Author, this);
public class A
{
public event EventHandler MyEvent;
}
public class B
{
public void AccessEvent (A a)
{
// Invalid - you can't raise an event which is declared in another class
a.MyEvent(this, EventArgs.Empty);
// Invalid - you can't copy the event either
EventHandler h = a.MyEvent;
h(this, EventArgs.Empty);
}
}Another alternative would be to pass the event itself into the ChangeNotifier object:But this won't work because a copy of the delegate list is created. That means if anyone adds event handlers later on, they won't be invoked when the property changes. So with that stuck firmly in my mind, I never gave much thought to removing that last remaining bit of boilerplate. That's about to change!new ChangeNotifier<string> (() =>Author, PropertyChanged);
public class Book : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
ChangeNotifier<string> author;
public string Author
{
get { return author.Value; }
set { author.Value = value; }
}
public Book()
{
author = ChangeNotifier.Create(() => Author, ????);
}
}
Func<PropertyChangedEventHandler> getter = delegate { return PropertyChanged; };
public class Book : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
ChangeNotifier<string> author;
public string Author {
get { return author.Value; }
set { author.Value = value; }
}
public Book()
{
author = ChangeNotifier.Create (() => Author, () => PropertyChanged);
}
}
public static class ChangeNotifier
{
public static ChangeNotifier<TValue> Create<TValue>(Expression<Func<TValue>> expression, Func<PropertyChangedEventHandler> notifier)
{
return new ChangeNotifier<TValue>(expression, notifier);
}
}
public class ChangeNotifier<TValue>
{
Func<PropertyChangedEventHandler> notifier;
string propertyName;
TValue value;
public TValue Value {
get { return value; }
set {
if (!EqualityComparer<TValue>.Default.Equals(this.value, value)) {
this.value = value;
// Get the current list of registered event handlers
// then invoke them with the correct 'sender' and event args
PropertyChangedEventHandler h = notifier();
if (h != null)
h(notifier.Target, new PropertyChangedEventArgs(propertyName));
}
}
}
public ChangeNotifier(Expression<Func<TValue>> expression, Func<PropertyChangedEventHandler> notifier)
{
if (expression.NodeType != ExpressionType.Lambda)
throw new ArgumentException("Value must be a lamda expression", "expression");
if (!(expression.Body is MemberExpression))
throw new ArgumentException("The body of the expression must be a memberref", "expression");
MemberExpression m = (MemberExpression)expression.Body;
this.notifier = notifier;
this.propertyName = m.Member.Name;
}
}
I have one final trick up my sleeve. Suppose you have a field (Progress) whose value is calculated based on other values (CurrentStep, TotalSteps) and you want to get Notifications whenever any of those fields changes, well, that's easy!public class Worker : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
ChangeNotifier<int> currentStep;
ChangeNotifier<int> totalSteps;
public int CurrentStep {
get { return currentStep.Value; }
set { currentStep.Value = value; }
}
public int TotalSteps {
get { return totalSteps.Value; }
set { totalSteps.Value = value; }
}
public double Progress
{
get { return (double)CurrentStep / TotalSteps; }
}
public Worker()
{
Func<PropertyChangedEventHandler> notifier = () => PropertyChanged;
currentStep = ChangeNotifier.Create(() => CurrentStep, notifier);
totalSteps = ChangeNotifier.Create(() => TotalSteps, notifier);
// A PropertyChanged notification will be created for Progress every time
// either the CurrentStep *or* TotalSteps changes.
ChangeNotifier.CreateDependent(
() => Progress,
notifier,
() => CurrentStep,
() => TotalSteps
);
}
}
public static class ChangeNotifier
{
static string GetPropertyName(Expression expression)
{
while (!(expression is MemberExpression)) {
if (expression is LambdaExpression)
expression = ((LambdaExpression)expression).Body;
else if (expression is UnaryExpression)
expression = ((UnaryExpression)expression).Operand;
}
return ((MemberExpression)expression).Member.Name;
}
public static void CreateDependent<TValue>(Expression<Func<TValue>> property, Func<PropertyChangedEventHandler> notifier, params Expression<Func<object>>[] dependents)
{
// The name of the property which is dependent on the value of other properties
var name = GetPropertyName(property);
// The names of the other properties
var dependentNames = dependents.Select<Expression, string>(GetPropertyName).ToArray();
INotifyPropertyChanged sender = (INotifyPropertyChanged)notifier.Target;
sender.PropertyChanged += (o, e) => {
// If one of our dependents changes, emit a PropertyChanged notification for our property
if (dependentNames.Contains(e.PropertyName)) {
var h = notifier();
if (h != null)
h(o, new PropertyChangedEventArgs (name));
}
};
}
public static ChangeNotifier<TValue> Create<TValue>(Expression<Func<TValue>> expression, Func<PropertyChangedEventHandler> notifier)
{
return new ChangeNotifier<TValue>(expression, notifier);
}
}
public string Title {
get { return title; }
set { title = value; }
}
public string Title {
get { return title.Value; }
set { title.Value = value; }
}
public class ChangeNotifier<TValue>
{
Action<string> notifyHandler;
string propertyName;
TValue value;
public TValue Value {
get { return value; }
set {
if (!EqualityComparer<TValue>.Default.Equals(this.value, value)) {
this.value = value;
notifyHandler(propertyName);
}
}
}
public ChangeNotifier(Expression<Func<TValue>> expression, Action<string> notifyHandler)
{
if (expression.NodeType != ExpressionType.Lambda)
throw new ArgumentException("Value must be a lamda expression", "expression");
if (!(expression.Body is MemberExpression))
throw new ArgumentException("The body of the expression must be a memberref", "expression");
MemberExpression m = (MemberExpression)expression.Body;
this.propertyName = m.Member.Name;
this.notifyHandler = notifyHandler;
}
}
public class Book : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
ChangeNotifier<string> author;
ChangeNotifier<decimal> price;
ChangeNotifier<int> quantity;
ChangeNotifier<string> title;
public string Author {
get { return author.Value; }
set { author.Value = value; }
}
public decimal Price {
get { return price.Value; }
set { price.Value = value; }
}
public int Quantity {
get { return quantity.Value; }
set { quantity.Value = value; }
}
public string Title {
get { return title.Value; }
set { title.Value = value; }
}
public Book()
{
Action<string> notify = (propertyName) => {
var h = PropertyChanged;
if (h != null)
h(this, new PropertyChangedEventArgs(propertyName));
};
author = new ChangeNotifier<string> (() => Author, notify);
price = new ChangeNotifier<decimal> (() => Price, notify);
quantity = new ChangeNotifier<int> (() => Quantity, notify);
title = new ChangeNotifier<string> (() => Title, notify);
}
}
I figured that since a slightly older version worked near-flawlessly, surely I could fix the live version with only a few minor tweaks. It's not like the would've completely rewritten the Chart controls within the space of 1 release.
Note the empty graphs. It doesn't look very pretty now, does it? However, one very minor fix later we now have the following:
Things are near-perfect in all the Data Visualization demos. One graph is missing a background colour and the elements in one graph aren't clickable when they should be. Neither should be particularly difficult to fix, the only problem is figuring out the cause.