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

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;
    }
}