this.DataBindings.Add(new Binding("Text", brain, "Dump"));

Development and .Net

  Home  |   Contact  |   Syndication    |   Login
  5 Posts | 0 Stories | 11 Comments | 4 Trackbacks

News

Archives

Post Categories

.Net Development

Wednesday, March 29, 2006 #

Last post I promised that I would post the final code that I was working on, in regards to Generics and DynamicMethods.  The basic idea of the code is a class that generates DynamicMethod delegates that have been casted as a certain type of delegate, using Generics.  Here's what I was thinking the calling code would look like

    TestDelegate
del = generator.SomeMethod<TestDelegate>();
Surprisingly, this was a lot hard than it first seems.  It seems that there are some restrictions with Generics and delegates.  First off, you can not use System.Delegate as a limiter for a Generic type.  So you can not do the following :
   
    public
T SomeMethod<T>() where T : System.Delegate
If you try to compile the previous code in some program you get the following error message: Constraint cannot be special class 'System.Delegate'.  Now DynamicMethod's CreateDelegate will only return an object of  type System.Delegate.  That is all and good, but if you are trying to have type safe delegate, you need to cast that object into the correct delegate type.  But the following code will also fail:

    return
(T)dynamicMethod.CreateDelegate(typeof(T));
That section of code will won't compile and will generate this error: Cannot convert type 'System.Delegate' to 'T'.  It seems that you have to use the as keyword to do a proper cast.

    return
dynamicMethod.CreateDelegate(typeof(T)) as T;
Now the code won't compile because The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint.  So you need to add a class constraint for T like

    private
T SomeMethod<T>() where T : class
Now once you set up your code in this way, the code will finally compile.  All this information about using Generics and delegates in this way comes from  Mike Woodring's post Constraints on contraints, I just applied his knowledge to DynamicMethods.

Here's the final class

    public class MethodGenerator
    {
        
public event EventHandler<CreatingMethodBodyEventArgs> CreatingMethodBody;
        
public MethodGenerator()
        {}

        
/// <summary>
        /// Generates a new method with a signature that matches the signature of the delegate T.
        /// </summary>
        /// <typeparam name="T">The type of the delegate to return and use as a method signature.</typeparam>
        /// <typeparam name="K">The type of the object with which the generated method will be associated.</typeparam>
        /// <param name="name">The identifying name of the generated method.</param>
        /// <returns>A delegate of type T which points to the generated method</returns>
        public T Generate<T, K>(string name) where T : class
        {
            
MethodInfo method = GetMethod(typeof(T));
            
ParameterInfo[] parameterDefinitions = method.GetParameters();
            
Type[] parameters = new Type[parameterDefinitions.Length];

            
for (int index = 0; index < parameterDefinitions.Length; index++)
            {
                parameters[index] = parameterDefinitions[index].ParameterType;
            }

            
return (ConstructDynamicMethod(name, method, typeof(T), parameters, typeof(K)).CreateDelegate(typeof(T)) as T);
        }

        
/// <summary>
        /// Generates a new method with a signature that matches the signature of the delegate T.
        /// </summary>
        /// <typeparam name="T">The type of the delegate to return and use as a method signature.</typeparam>
        /// <typeparam name="K">The type of the object with which the generated method will be associated.</typeparam>
        /// <param name="name">The identifying name of the generated method.</param>
        /// <param name="instance">The instance of type K to which the method will be bound.</param>
        /// <returns>A delegate of type T which points to the generated method</returns>
        public T Generate<T, K>(string name, K instance) where T : class
        {
            
MethodInfo method = GetMethod(typeof(T));
            
ParameterInfo[] parameterDefinitions = method.GetParameters();
            
Type[] parameters = new Type[parameterDefinitions.Length + 1];

            parameters[0] =
typeof(K);
            
for (int index = 0; index < parameterDefinitions.Length; index++)
            {
                parameters[index + 1] = parameterDefinitions[index].ParameterType;
            }

            
return (ConstructDynamicMethod(name, method, typeof(T), parameters, typeof(K)).CreateDelegate(typeof(T), instance) as T);
        }

        
private DynamicMethod ConstructDynamicMethod(string name, MethodInfo method, Type delegateType, Type[] parameters, Type ownerType)
        {
            
DynamicMethod dm = new DynamicMethod(name, method.ReturnType, parameters, ownerType);
            
ILGenerator il = dm.GetILGenerator();

            
if (CreatingMethodBody != null)
            {
                CreatingMethodBody(
this, new CreatingMethodBodyEventArgs(name, delegateType, ownerType, il));
            }
            
else
            {
                il.Emit(
OpCodes.Ret);
            }

            
return dm;
        }

        
private MethodInfo GetMethod(Type delegateType)
        {
            
if (!delegateType.IsSubclassOf(typeof(Delegate)))
            {
                
throw new ArgumentException("Type T must be a delegate", "T");
            }
            
return delegateType.GetMethod("Invoke");
        }
    }

    
public class CreatingMethodBodyEventArgs : EventArgs
    {
        
private string _name;
        
private Type _delegateType;
        
private Type _ownerType;
        
private ILGenerator _generator;

        
public Type DelegateType
        {
            
get { return _delegateType; }
        }

        
public ILGenerator ILGenerator
        {
            
get { return _generator; }
        }

        
public Type OwnerType
        {
            
get { return _ownerType; }
        }

        
public string Name
        {
            
get { return _name; }
        }

        
public CreatingMethodBodyEventArgs(string name, Type delegateType, Type ownerType, ILGenerator ilGenerator)
        {
            _name = name;
            _generator = ilGenerator;
            _delegateType = delegateType;
            _ownerType = ownerType;
        }
    }
In the Generate function the first generic type parameter specifies the delegate to imitate.  The second generic type parameter is the "owner" of the generated function.  The return value will be a strongly typed delegate (no more Invoke(new object[]... stuff).  When it is time to generate the body of the method, the class fires off an event (CreateingMethodBody), that allows the caller to specify the body of the method.  Unfortunately you still have to use the ILGenerator class to generate the method body.  There are still some issues with the above code too.  
  1).  If T is a type other than System.Delegate or subclass, it will throw a runtime error not a compile time error.  This is because there is no way to constraint the type parameter to only System.Delegate.  It just isn't allowed.
  2)  If the caller of the class doesn't specify a method body or specifies an incorrect body (wrong return type, incorrect code, etc), the error won't be caught until the CreateDelegate method is called on the DynamicMethod, so runtime again, not compile time.
  3)  I still haven't thought of a good use for it ;)  


Friday, March 17, 2006 #

test Recently I had one of those aggrivating, head pounding, pick up the monitor and throw it through the window, wall, co-worker, whatever is closest.  The issue involved using DynamicMethods, CreateDelegate method, and binding the DynamicMethod to an object.  I was working on an experiment with DynamicMethods and Generics (thats for another post), when I encountered the problem.  Thinking it was some issue to do with the Generics (which there are a lot), I worked on a test code that had nothing to do with Generics and still ran into the problem.

Now according to the .Net documentation the first parameter of the DynamicMethod, when used in CreateDelegate bound to an object, has to be the same type as the object  to which the method is being bound.  
Parameters for overloaded CreateDelegate method of DynamicMethod:
    delegateType
   
A delegate type whose signature matches that of the dynamic method, minus the first parameter.

    target
   
An object the delegate is bound to. Must be of the same type as the first parameter of the dynamic method.

Now here is code similar to what i was working on.

    public delegate void TestingDelegate(Form instance, int value);

    
public class BadGenerator
    {
        
public Delegate Generate(Form instance)
        {
            
Type[] parameters = new Type[2];
            parameters[0] =
typeof(Form);
            parameters[1] =
typeof(int);

            
DynamicMethod dm = new DynamicMethod("", typeof(void), parameters, typeof(Form));
            
ILGenerator il = dm.GetILGenerator();
            
            il.Emit(
OpCodes.Ret);

            
return dm.CreateDelegate(typeof(TestingDelegate), instance);
        }
    }


Now I imagine that some of the more observent of you have already noticed my simple but stupid mistake.  Whenever I ran ithis code, it would throw an exception when it tries to execute the CreateDelegate function.  It would always throw System.ArgumentException "Error binding to target method."  Not exactly the most descriptive error message.  I tiried lots of things to get it to work, even used Reflector to trace the calls (no luck, it ends up calling some Win32 APIs).  I spent too much time trying to figure this problem out, without any luck.  Then I happened to be rereading the documentation, for the tenth time it seemed, when I saw this in the Remarks about the CreateDelegate function:

This method overload requires target to be of the same type as the first parameter of the dynamic method, or to be assignable to that type (for example, a derived class). The signature of delegateType has all the parameters of the dynamic method except the first. For example, if the dynamic method has the parameters String, Int32, and Byte, then delegateType has the parameters Int32 and Byte; target is of type String.

I've bolded the most important sentence.  When I read that, I felt like an idiot, a very very very big idiot.
The corrected code would be:

    public delegate void TestingDelegate(int value);

    
public class BadGenerator
    {
        
public Delegate Generate(Form instance)
        {
            
Type[] parameters = new Type[2];
            parameters[0] =
typeof(Form);
            parameters[1] =
typeof(int);

            
DynamicMethod dm = new DynamicMethod("", typeof(void), parameters, typeof(Form));
            
ILGenerator il = dm.GetILGenerator();
            
            il.Emit(
OpCodes.Ret);

            
return dm.CreateDelegate(typeof(TestingDelegate), instance);
        }
    }

I had to change the delegate from ( public delegate void TestingDelegate(Form instance, int value); ) to ( public delegate void TestingDelegate(int value); ), because a DynamicMethod bound to an object always takes one more parameter than the delegate it is referencing.  The other solution would be to define another parameter for the DynamicMethod, so the parameter list would be (Form, Form, int).

So what is the moral of the story:

Always Read The Documentation, Completely.

Monday, January 30, 2006 #

The TestDriven.net vs addin now has code coverage for all of us that can't afford Team Suite.  The addin now plays nicely with the open source code coverage tool NCover.  For those of you who don't know what TestDriven.Net is, its a addin for Visual Studio that allows you to run your unit test directly from the IDE.  It supports NUnit, MbUnit, and even Team Suite Tests.  And the best feature is that you can even debug your unit tests.  Jamie just keeps making TestDriven.Net better and better.  If you don't have it yet, go get it.
    Original Article: Code Coverage for The People!

I've been reading about one of the obscure new features of .Net 2.0 called DynamicMethods.  Part of the System.Reflection.Emit namespace, the DynamicMethod class allows you to create and execute methods at runtime without creating any dynamic assembly or dynamic type.

This by itself sounds interesting, but not very useful.  I mean how often do you have the need to dynamic generate methods?  Except for certain situations, not very often if at all.  But DynamicMethods have one other interesting feature. If the DynamicMethod is bound to a particular Type, then the generated method has access to the private members of that type.

Here's a small example (important code only):

public partial class Form1 : Form
    {
        
DynamicMethod _dMethod;

        
public Form1()
        {
            InitializeComponent();
            _dMethod = CreateDynamicMethod();
        }

        
private DynamicMethod CreateDynamicMethod()
        {
            
DynamicMethod dmethod = new DynamicMethod("", typeof(int), new Type[] { typeof(TextBoxBase) }, typeof(TextBoxBase));
            
ILGenerator igen = dmethod.GetILGenerator();

            
FieldInfo field = typeof(TextBoxBase).GetField("maxLength", BindingFlags.NonPublic | BindingFlags.Instance);

            igen.Emit(
OpCodes.Ldarg_0);
            igen.Emit(
OpCodes.Ldfld, field);
            igen.Emit(
OpCodes.Ret);

            
return dmethod;
        }

        
private void button1_Click(object sender, EventArgs e)
        {
            
Random r = new Random();
            textBox1.MaxLength = r.Next(1, 4096);

            label1.Text =
"Max Length = " + textBox1.MaxLength.ToString();
        }

        
private void button2_Click(object sender, EventArgs e)
        {
            
MessageBox.Show("Max Length = " + ((int)_dMethod.Invoke(textBox1, new object[] { textBox1 })).ToString());
        }
    }

This is a simple Windows Application that contains one TextBox, one Label, and two Buttons.  The first button (button2) sets the MaxLength property of the TextBox to a random number between 1 and 4096 and sets the Label's Text property to that value.  Then second button executes the generated function that was generated at the form's creation and shows the returned value in a MessageBox.  Now note what the generated function is doing, specifically this line:

    FieldInfo
field = typeof(TextBoxBase).GetField("maxLength", BindingFlags.NonPublic | BindingFlags.Instance);

The function is using a private instance field called maxLength, which belongs to the abstract class TextBoxBase (I used Lutz'a Reflector to find out what the name of the field was).  The function then just returns the value of the field.  So the generated function was able to access the pri
vate instance field for the abstract parent of the TextBox object.

There are two drawbacks with these DynamicMethods when compared to the Extension Methods of C# 3.0.

1)  You must have a reference to the created DynamicMethod object or the delegate created by the DynamicMethod's CreateDelegate function.
2)  You have to use IL OpCodes to generate the body of your runtime function.

Now I have to think up some practical examples (not counting dynamic delegates for events).

Some interesting links:
    DebuggerVisualizer for DynamicMethod (Show me the IL)
    To generate an event handler at run time by using a dynamic method
    Fast late-bound invocation through DynamicMethod delegates

Here's the full code from the example above:

using
System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Windows.Forms;

namespace DynamicMethodTest
{
    
public partial class Form1 : Form
    {
        
DynamicMethod _dMethod;

        
public Form1()
        {
            InitializeComponent();
            _dMethod = CreateDynamicMethod();
        }

        
private DynamicMethod CreateDynamicMethod()
        {
            
DynamicMethod dmethod = new DynamicMethod("Hello", typeof(int), new Type[] { typeof(TextBoxBase) }, typeof(TextBoxBase));
            
ILGenerator igen = dmethod.GetILGenerator();

            
FieldInfo field = typeof(TextBoxBase).GetField("maxLength", BindingFlags.NonPublic | BindingFlags.Instance);

            igen.Emit(
OpCodes.Ldarg_0);
            igen.Emit(
OpCodes.Ldfld, field);
            igen.Emit(
OpCodes.Ret);

            
return dmethod;
        }

        
private void button1_Click(object sender, EventArgs e)
        {
            
Random r = new Random();
            textBox1.MaxLength = r.Next(1, 4096);

            label1.Text =
"Max Length = " + textBox1.MaxLength.ToString();
        }

        
private void button2_Click(object sender, EventArgs e)
        {
            
MessageBox.Show("Max Length = " + ((int)_dMethod.Invoke(textBox1, new object[] { textBox1 })).ToString());
        }

        
/// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        
/// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            
if (disposing && (components != null))
            {
                components.Dispose();
            }
            
base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        
/// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            
this.textBox1 = new System.Windows.Forms.TextBox();
            
this.button1 = new System.Windows.Forms.Button();
            
this.button2 = new System.Windows.Forms.Button();
            
this.label1 = new System.Windows.Forms.Label();
            
this.SuspendLayout();
            
//
            // textBox1
            //
            this.textBox1.Location = new System.Drawing.Point(13, 13);
            
this.textBox1.Name = "textBox1";
            
this.textBox1.Size = new System.Drawing.Size(100, 20);
            
this.textBox1.TabIndex = 0;
            
//
            // button2
            //
            this.button2.Location = new System.Drawing.Point(205, 9);
            
this.button2.Name = "button1";
            
this.button2.Size = new System.Drawing.Size(75, 23);
            
this.button2.TabIndex = 1;
            
this.button2.Text = "Execute";
            
this.button2.UseVisualStyleBackColor = true;
            
this.button2.Click += new System.EventHandler(this.button2_Click);
            
//
            // button1
            //
            this.button1.Location = new System.Drawing.Point(205, 39);
            
this.button1.Name = "button2";
            
this.button1.Size = new System.Drawing.Size(75, 23);
            
this.button1.TabIndex = 2;
            
this.button1.Text = "Set Length";
            
this.button1.UseVisualStyleBackColor = true;
            
this.button1.Click += new System.EventHandler(this.button1_Click);
            
//
            // label1
            //
            this.label1.AutoSize = true;
            
this.label1.Location = new System.Drawing.Point(13, 48);
            
this.label1.Name = "label1";
            
this.label1.Size = new System.Drawing.Size(75, 13);
            
this.label1.TabIndex = 3;
            
this.label1.Text = "Max Length = ";
            
//
            // Form1
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            
this.ClientSize = new System.Drawing.Size(292, 273);
            
this.Controls.Add(this.label1);
            
this.Controls.Add(this.button2);
            
this.Controls.Add(this.button1);
            
this.Controls.Add(this.textBox1);
            
this.Name = "Form1";
            
this.Text = "Form1";
            
this.ResumeLayout(false);
            
this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.TextBox textBox1;
        
private System.Windows.Forms.Button button1;
        
private System.Windows.Forms.Button button2;
        
private System.Windows.Forms.Label label1;
    }
}

Friday, January 20, 2006 #

Does anyone know of any open source podcast clients written in .net? I've been searching for the past hour or so and haven't found anything. There are a lot of free clients out there (and I mean a lot) for .net, but they seem to be lacking a couple of vital features here and there. I need a podcast client that:

1) Supports Proxy and Proxy Authenication (really really annoyed that the place I work is enforcing this)

2) Save files to subscription specific folders (that is one folder per podcast)

But I haven't found anything that does both. Most clients have proxy support, but almost none have complete proxy authenication. The one client that I've found that has proxy authenication, doesn't save the podcast to different folders. And frankly I can't live without my weekly Twit fix.

*Sigh* This is the type of situation that prompts programmers to roll there own, but I'm lazy and the siren call of AoE3 is strong.