Friday, November 20, 2009

Programming Challenge 2 - Falling

After the success of Programming Challenge 1 I finally present Programming Challenge 2. The intent of this challenge is to create a program that uses multi-threading.

Here is the Challenge:

Write a winform ASCII game that with these properties:

  • It has a game grid that is 5x5.
  • The player should be represented as a plus sign “+” located in the second row from the top.
  • The player is able to move the + from side to side.
  • A row of 4 minus signs “-” and one blank space, should be randomly generated at the bottom of the 5x5 grid, and move up (giving the perception that the user is falling down).
  • Every 1/4 of a second, the row of minuses should move up one row until it disappears off the top of the grid and another 1/4 second late, a new one will appear at the bottom.
  • Play continues until the + sign attempts to occupy the same space as a minus sign. When this happens, the minus sign should be replaced by an “X”. This can happen by either:
    • falling down on top of a minus.
    • Or moving right or left into a minus. After play ends, display a score to the player which is the number of minus rows successfully avoided.

Should look something like this when completed:

And with an end game that looks like this:



Good luck and happy falling!

* Legal Notice * These Programming Challenges

shall not be reproduced in any form without my express written consent.

Saturday, October 24, 2009

Programming Challenge 1 - Mow the Lawn - Results

There were three projects submitted.  One by a brand new .Net developer, Brandon, one by a C#/Flash developer, Kyle, and another one by myself.

This programming challenge presented some common problems that had to be over come like how store the grass, the position of the lawnmower, and what the grass should be after it has mowed.  Some solutions for these problems were extremely similar and others were surprisingly different.

Brandon’s code was the shortest and the simplest taking around 120 lines of code.  He used a two dimensional char array to store the lawn and class level variables to store the position of the lawn mower, and what the grass under the mower is.  This is his code to handle the move with the other 3 directions taken out.

private void MowerForm_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Down:
if (MowerY < 9)
{
grassArray[MowerY, MowerX] = underMower;
MowerY++;
checkCutStatus();
}
break;
}
PrintGrass();

if (numcut == 100)
MessageBox.Show("The lawn is mowed!! Daryl's mom is lathered up and waiting to congratulate you inside!");

}


It’s very simple, first checking to be able to move in that direction and then updating the lawn under the mower, and moving the mower.  His checkCutStatus() would then update underMower, and place the mower at the new location.  Pretty simple but I would also add an else to the if, that had a return statement.  That way if you couldn’t move, you wouldn’t reprint the grass.  Whenever a piece of grass goes from medium to short, the numcut get incremented and once all 100 are cut, the congratulations message gets displayed.  I would suggest at least making it a class level constant for the 100 and the 9.  It makes it easier to update and (numcut == AllGrass) makes more sense then (numcut == 100).



Kyle’s code was the second shortest.  He also used a two dimensional array but he created a Grass class that kept track of the grass type, Label, and whether it had been marked as counted.  Below is his move function. 



protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
switch (keyData)
{
case Keys.Right:
if (mower_location.X < lawn_width - 1)
{
mower_location.X += 1;
update_grass();
}
break;
}
return true;
}



update_grass() keeps track of the old mower position and handles cutting the grass, moving the lawn mower, and keeping track of the previous position.  One of the more unique aspects of Kyle’s program is that rather than having one label with 100 squares, he has 100 labels.  This makes it easier and quicker to update since only the text that gets changed needs to be updated.  Brandon and my solution requires the entire 100 squares to be redrawn.



My code was the longest, and with the most extra classes, but since I knew what the next programming challenge would be, I thought it would be worth it to add that additional complexity.  Instead of using a 2 dimensional array, I used an array of an array incase I wanted the yard to not be square. Below is my move function.



private void Form1_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Down:
case Keys.Up:
case Keys.Left:
case Keys.Right:
if (Lawn.Move(Mower, GetDirection(e.KeyCode)))
{
lblLawn.Text = Lawn.ToString();

if (DisplayMessage && Lawn.IsMowed)
{
MessageBox.Show("Have a Lemonade, you're finished!");
DisplayMessage = false;
}
}
break;

default:
break;
}
e.Handled = true;
}



I actually pulled out the lawn logic out of my main form in an effort to make it more MVC-ish.  The Lawn.Move() returns true if the object that is attempted to be moved, was move.  Similar to Brandon’s counting of cut grass, the Lawn class keeps track of the grass left uncut, and encapsulates it with the IsMowed property.  The main work of moving the mower around is handled in Lawn.Move().



public static bool Move(LawnBase obj, Direction direction)
{
if (!obj.IsMovable) { return false; }

LawnBase nextObject = GetObjectInDirection(obj, direction);

if (nextObject == null || !nextObject.CanMoveOver) { return false; }

var mobileObject = ((MoveableLawnBase)obj);
_lawn[obj.Row][obj.Column] = mobileObject.GroundBeneath;
mobileObject.Move(direction, nextObject);
DecrementGrassCount(nextObject, mobileObject.GroundBeneath);
_lawn[obj.Row][obj.Column] = obj;

return true;
}


There are some additional checks to see if an object can be moved over, so later if we add more objects to our yard, they’ll be able to interact nicely.



Feel free to comment:

Monday, October 12, 2009

Programming Challenge 1 - Mow the Lawn

A friend of mine has just switched from developing in COBALT, to C#. As a fun way to help him learn C# and to argue over better methods of programming, we decided to make little programming challenges that were designed to be short, simple and fun. This contest is open to anyone, just feel free to e-mail me your zipped project solution and I'll post it and comment on it.

So here goes the first challenge:
Write a winform ASCII lawn mowing application with these properties:
  • It should start with a 10x10 grid of capital "X"'s.
  • The lawn mower should be represented by an "@" symbol, and start in the upper left hand corner.
  • The user should be able to use the arrow keys to move the lawnmower around the lawn.
  • The first time the lawn mower passes over an X, it gets converted to a lower case "v".
  • When the lawn mower passes over a "v", it gets converted to a ".".
  • When the entire lawn has been reduced to "."'s a message should be displayed to the user.

Good luck and happy mowing!

* Legal Notice * These Programming Challenges shall not be reproduced in any form without my express written consent.


Wednesday, July 8, 2009

Visual Studio Region Snippet

I like to format my regions, so the region name is at the bottom of the region, that way I don't have to scroll all the way to the top to see the next region. I've been doing this by hand all the time, and figured it was about time for me to create a code snippet for it.

Using this telerick blog, I was able to see the folder that all the snippets are stored ("<Visual Studio installation directory>\VC#\Snippets\1033\Visual C#") and then the rest was easy.



<?xml version="1.0" encoding="utf-8" ?>
CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<
CodeSnippet Format="1.0.0">
<
Header>
<
Title>region</Title>
<
Shortcut>region</Shortcut>
<
Description>Code snippet for region</Description>
<
Author>Daryl LaBar</Author>
<
SnippetTypes>
<
SnippetType>Expansion</SnippetType>
<
SnippetType>SurroundsWith</SnippetType>
</
SnippetTypes>
</
Header>
<
Snippet>
<
Declarations>
<
Literal>
<
ID>name</ID>
<
ToolTip>Region name</ToolTip>
<
Default>MyRegion</Default>
</
Literal>
</
Declarations>
<
Code Language="csharp"><![CDATA[#region $name$

$selected$$end$

#endregion // $name$
]]>
</
Code>
</
Snippet>
</
CodeSnippet>
CodeSnippets>


Wednesday, May 27, 2009

Yield Passes Control Through Intermediate Calls

I was wanting to be able to perform a backend database service in a batch mode, but wanted to make the actual batch processing part of it as encapsulated as possible.

I created a BO that returned an IEnumerable<List<MyBO>>, but it didn't use any yield statements, it just called a data access layer that was using the yield statements. I was concerned if this was going to work, so I decided to test it using snippet compiler.

So this is my quick and dirty test to see if it would work:

public static void RunSnippet()
{
foreach (List<string> answers in GetAnswers())
{
WL(answers.Count);
foreach (string answer in answers)
{
WL(answer);
}
}
}

public static IEnumerable<List<string>> GetAnswers()
{
return GetTheAnswers();
}

private static IEnumerable<List<string>> GetTheAnswers()
{
List<string> answers = new List<string>();
answers.Add("A");
answers.Add("B");
answers.Add("C");
answers.Add("D");
yield return answers;

answers = new List<string>();
answers.Add("D");
answers.Add("C");
answers.Add("B");
answers.Add("A");
yield return answers;

answers = new List<string>();
answers.Add("X");
answers.Add("Y");
answers.Add("Z");
yield return answers;
}






And these were the results:
4
A
B
C
D
4
D
C
B
A
3
X
Y
Z


So even though GetAnswers() gets called once, GetTheAnswers() will return control back to the enumerating foreach multiple times.

Wednesday, May 13, 2009

Searching Through All Controls in a ControlCollection

I had a user control at work that dynamically added TextBoxes to a HtmlTable.  After figuring out how to even access the dynamic controls, I needed to be able to retrieve them easily.  Since the ids were based on a primary key id from the database, I couldn't determine what the ids of the TextBoxes were using the FindControl() method.  I could hit the database again to find the ids and then use the FindControl method, but I didn't like having another database hit if I could help it.

What I wanted to be able to do was enumerate over all of the controls located within my user control.  After thinking about it for a bit, I decided to finally make use of the C# "yield" statement and create my own recursive iterator method:

public static IEnumerable<Control> GetAllControls(this ControlCollection controls)
{
foreach (Control control in controls)
{
yield return control;

foreach(Control childControl in control.Controls.GetAllControls()){
yield return childControl;
}
}
}

Now I could do a foreach against all controls within a ControlCollection, and process any TextBoxes:

foreach (Control control in PlaceHolder1.Controls.GetAllControls())
{
if (control.GetType() == typeof(TextBox))
{
// Do Work Here
}
}

I believe that this could be extremely useful and wonder why it wasn't included in .Net to begin with.

ASP.Net Control to Display Session

I wanted to be able to see all my Session data on my webpage, but only when I actually had a debugger attached.  My solution was a two step approach:
  1. Create a user control that ouputs the session into a table
  2. Add that user control to the to the page if a debugger is attached
To accomplish step 1, I created a user control which is located below.  It overrides the Render method, directly writing a text html table to the HtmlTextWriter.  It contains two other methods, one to create the table to display the session information in, iterating over all the values in the session, and another that uses reflection to output the value of all properties of non-simple types.  

public class SessionDisplayControl : UserControl

public SessionDisplayControl()
{
}

protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
writer.Write(GetOutputState());
}

protected string GetOutputState()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder(2000);
sb.AppendLine("<table border='1' align='center'><tr><td colspan='2'>There are " + Session.Contents.Count + " Session variables</td></tr>");
foreach (string name in Session.Contents)
{
System.Collections.IEnumerable enumeratable = Session[name] as System.Collections.IEnumerable;
if (enumeratable == null || Session[name].GetType() == typeof(String))
{
sb.AppendLine(string.Format(@"<tr><td>{0}</td><td>{1}</td></tr>", HttpUtility.HtmlEncode(name), GetObjectValue(Session[name] ?? "<NULL>")));
}
else
{
sb.AppendLine(@"<tr><td>" + HttpUtility.HtmlEncode(name) + @"</td><td><table border='1'>");
int i = 0;
foreach (object o in enumeratable)
{
sb.AppendLine(string.Format("<tr><td>Item({0})</td>", i++));
sb.AppendLine(string.Format(@"<td>{0}</td></tr>", GetObjectValue(o)));
}
sb.AppendLine(@"</table></tr>");
}
}
sb.AppendLine("</table>");
return sb.ToString();
}

private static string GetObjectValue(Object obj)
{
if (obj is string || obj is bool || obj is int || obj is long || obj is double || obj is decimal || obj is DateTime)
{
return HttpUtility.HtmlEncode(obj.ToString());
}

System.Text.StringBuilder sb = new System.Text.StringBuilder(500);
System.Reflection.PropertyInfo[] properties = obj.GetType().GetProperties();
foreach (System.Reflection.PropertyInfo property in properties)
{
try
{
sb.Append(HttpUtility.HtmlEncode(property.Name));
sb.Append(": ");
sb.Append(HttpUtility.HtmlEncode((property.GetValue(obj, null) ?? "<NULL>").ToString()));
sb.Append("<br/>");
}
catch (Exception ex)
{
sb.Append("ERROR: " + HttpUtility.HtmlEncode(ex.Message) + "<br/>");
}
}
return sb.ToString();
}
}

To accomplish step 2, all that was needed was to override the OnLoad event in my BasePage class from which all other classes in my website inherit, check for a debugger, and add my user control from step 1 to the page if one was attached.

    protected override void  OnLoad(EventArgs e)
{
base.OnLoad(e);
if (System.Diagnostics.Debugger.IsAttached && !IsPostBack)
{
this.Controls.Add(new SessionDisplayControl());
}
}

Now whenever I have a debugger attached my session will be displayed at the bottom of the page.  This has helped me clean up unused session variables numerous times.  Enjoy!

Accessing Controls Dynamically Created During AJAX PartialPostBacks

I had an extremely annoying issue trying to be able to create textboxes dynamically within an AJAX UpdatePanel during on the PostBack.  Below is the code that I used to create a textbox, and added it to a TableCell.  It runs during an AJAX PartialPostback created on a DropDownList SelectedIndexChangedEvent.

TextBox answerText = new TextBox();
answerText.ID = "AnswerTextBox" + question.ServiceRequestQuestionId;
answerText.Attributes.Add("questionid", question.ServiceRequestQuestionId.ToString());
answerText.TextMode = TextBoxMode.MultiLine;
answerText.Style.Add(HtmlTextWriterStyle.Width, "100%");
answerText.Columns = 4;
answerText.Rows = 4;
answerText.Attributes.Add("runat", "server");
answerCell.Controls.Add(answerText);



My TextBox was showing up exactly how I wanted it to, but when I'd attempt to access the textbox after the user clicked the button submit, the textbox didn't exist anywhere on my page.  After googling for a bit, it made sense to me that I had to re-add my dynamic control to my page, but what I couldn't find was how to access the answerText.Text value from the ViewState.  I finally discovered that the TextBox had to be readded during the Page_Load or Page_Init events in order to be able to access their ViewState values, and it is only after the Page_Load event finishes that the values actually become accessible, and only if you add the objects in the some location within the heirarchy, and with the same id value.

I was thinking that somehow automagically I should be able to access this controls from the ViewState, and when I readded the control, it would override the value.  Boy was I wrong. 

Friday, April 24, 2009

Windows Registry Keys For Adding Register Dll/OCX Shortcut

Anyone that has ever had to deal with registering dlls knows that it is just plan annoying to have to type "regsvr32.exe" every single time you want to register a dll.  I'm a lazy programmer.  If I have to do something twice, that's once too many.  So I don't have to look all over the internet for the correct registery settings to perform this change, I'm posting it here on my blog.

Paste this text into a new text document, save it with a .reg extension, double click it, and select that "Yes" you "are sure you want to add the information in to the registry".  Done.


[HKEY_CLASSES_ROOT\dllfile\Shell]
@="Register"

[HKEY_CLASSES_ROOT\dllfile\Shell\Register]

[HKEY_CLASSES_ROOT\dllfile\Shell\Register\command]
@="regsvr32.exe \"%1\""

[HKEY_CLASSES_ROOT\dllfile\Shell\Unregister]

[HKEY_CLASSES_ROOT\dllfile\Shell\Unregister\command]
@="regsvr32.exe /u \"%1\""

[HKEY_CLASSES_ROOT\.ocx]
@="ocxfile"

[HKEY_CLASSES_ROOT\ocxfile]
@="OCX File"
"EditFlags"=hex:00,00,01,00

[HKEY_CLASSES_ROOT\ocxfile\Shell]
@="Register"

[HKEY_CLASSES_ROOT\ocxfile\Shell\Register]

[HKEY_CLASSES_ROOT\ocxfile\Shell\Register\command]
@="regsvr32.exe \"%1\""

[HKEY_CLASSES_ROOT\ocxfile\Shell\Unregister]

[HKEY_CLASSES_ROOT\ocxfile\Shell\Unregister\command]
@="regsvr32.exe /u \"%1\""

Saturday, March 21, 2009

String Construtor String(Char[] value) doesn't work?

So for some reason that I can't determine I'm unable to use the New String(char[] value) function.  I tried this expression in my immediate window while debugging a Windows Mobile 6 .Net 3.5 application, and this is what I got:

?new string(new char[]{'1'})
A first chance exception of type 'System.InvalidOperationException' occurred in mscorlib.dll
'new string(new char[]{'1'})' threw an exception of type 'System.InvalidOperationException'
    base {System.SystemException}: {"InvalidOperationException"}

If I typed the same expression into a normal C# desktop app .Net 3.5 application, this is what I got:

?new string(new char[]{'1'})
'new string(new char[]{'1'})' threw an exception of type 'System.ArgumentException'
    base {System.SystemException}: {"Only NewString function evaluation can create a new string."}
    Message: "Only NewString function evaluation can create a new string."
    ParamName: null

I'm exteremly puzzeled.  Anyone have any ideas?

Saturday, February 7, 2009

Performing Asynchronous XML Serialization

If you work at a company anything like mine, you've had to deserialize a large XML file, and been forced to sit there for the Deserialize() method to complete. I haven't done a lot of work with threading, but I figured now was the time to start.

I decided to wrap the normal System.Xml.Serialization.XmlSerializer in a generic class that would encapsulate the threading. I also wanted it to perform some logging if there were issues during deserialization. The process starts with a factory method to return the serializer object.

        public static LoggedXMLSerializer<T> RunDeserializeAsync(string filePath) {
return new LoggedXMLSerializer<T>(filePath);
}


Which immediately calls the private constructor:

        private LoggedXMLSerializer(string filePath)
:
this() {
DeserializingDelegate = PeformDeserialization;


FileSize = new FileInfo(filePath).Length;
// The XML File tends to take up 110% of space in memory as it does on disc
FileSize = (long)(FileSize * 1.10);

TotalMemoryIncrease = 0;

Result = DeserializingDelegate.BeginInvoke(filePath, null, null);
StartingMemorySize = GetCurrentProcessMemoryInUse();
}




Which first marks the PerformDeserialization() method as the DeserializationDelegate. It then determines the file size that is being opened to be used later to determine the progress that is left. For now, it only deserializes an actual file, but it could be extended with the other default constructors of the XmlSerializer.

BeginInvoke() is called on the DeserializationDelege, which starts the deserialization and returns an IAsyncResult object. BeginInvoke() starts a new thread, and calls the assigned Delegate. You can then query the IAsyncResult object to see if it finished, or just call EndInvoke() and your primary thread will wait until the secondary thread finishes.

Immediately after the thread is created, the current size of the process is stored to also be used later to determine the progress left.

The PerformDeserialization() method is exactly what you would do if you weren't invoking it on a separate thread. Create an XmlSerialization object,assign an Event Handler for loading issues, open a file with a StreamReader and call Deserialize().

        private T PeformDeserialization(string filePath) {
XmlSerializer xs = new XmlSerializer(typeof(T));
xs.UnknownNode += new XmlNodeEventHandler(Xs_UnknownNode);

StreamReader reader = File.OpenText(filePath);

return (T)xs.Deserialize(reader);
}


Since the Deserialize() method is still synchronous, the hardest part has been coming up with a good method of determining the progress of the file load. This is what I came up with, but if you, dear reader, have a better idea, I'd like to hear it.

        /// <summary>
/// Best guess at progress based on the size of the file, and the amount of increase in the memory of the process
/// </summary>
public int Progress {
get {
if (Result.IsCompleted) {
return 100;
}
long currentSize = GetCurrentProcessMemoryInUse();
if (currentSize < StartingMemorySize + TotalMemoryIncrease) {
// For Some reason, the current size of memory is smaller than the starting size plus the increase in memory usage
// Assume it is due to some garbage collection in between calls to Progress
// Update the starting memory size so it is equal to the current + total increase
// This assumes that no additional memory was used to deserialize the XML
StartingMemorySize = currentSize - TotalMemoryIncrease;
}
else {
TotalMemoryIncrease = currentSize - StartingMemorySize;
}

int tempProgress = (int)((currentSize - StartingMemorySize) / (double)FileSize * 100);
if (tempProgress < 0) {
tempProgress = 0;
}

if (tempProgress > 125) {
// Must have had a bad starting point, move it back to 75%
StartingMemorySize = (int)(currentSize - .75 * FileSize);
// Reinitialize Total Memory
TotalMemoryIncrease = currentSize - StartingMemorySize;
tempProgress = 75;
}

if (tempProgress > 100) {
tempProgress = 99;
}
return tempProgress;
}
}



The first thing it does, is check the IAsyncResult object to see if it has completed, if it has, then it returns 100%. Done. The next part I added later when I noticed that if I opened up more than one file, the progress of the second file would move to about 25%, then it would drop down to near 0, and stay there until it finished. I'm guessing it is due to the garbage collector collecting a large amount of memory due to the first deserialized object being release. The basic method of determining progress is then calculated, assume that the deserialized XML, will take up nearly the same amount as the serialized, is then performed. Get the increase in memory size since first beginning to deserialize the Xml, and divide it by the size of the file. Then do some checking to see if the progress has grown too large, or is over a 100%. It is not a perfect solution, but was extremely simple to implement, and serves my needs well.

Below is the entire class. Feel free to make comments.

    public class LoggedXMLSerializer<T> {
private delegate T Deserializer (string path);

private Deserializer DeserializingDelegate { get; set; }
private IAsyncResult Result { get; set; }
private long StartingMemorySize { get; set; }
private long TotalMemoryIncrease { get; set; }
private long FileSize { get; set; }
private Dictionary<string, string> XmlUnknowns {get; set;}

#region Public Properties

public T Xml { get; protected set;}

/// <summary>
/// Best guess at progress based on the size of the file, and the amount of increase in the memory of the process
/// </summary>
public int Progress {
get {
if (Result.IsCompleted) {
return 100;
}
long currentSize = GetCurrentProcessMemoryInUse();
if (currentSize < StartingMemorySize + TotalMemoryIncrease) {
// For Some reason, the current size of memory is smaller than the starting size plus the increase in memory usage
// Assume it is due to some garbage collection in between calls to Progress
// Update the starting memory size so it is equal to the current + total increase
// This assumes that no additional memory was used to deserialize the XML
StartingMemorySize = currentSize - TotalMemoryIncrease;
}
else {
TotalMemoryIncrease = currentSize - StartingMemorySize;
}

int tempProgress = (int)((currentSize - StartingMemorySize) / (double)FileSize * 100);
if (tempProgress < 0) {
tempProgress = 0;
}

if (tempProgress > 125) {
// Must have had a bad starting point, move it back to 75%
StartingMemorySize = (int)(currentSize - .75 * FileSize);
// Reinitialize Total Memory
TotalMemoryIncrease = currentSize - StartingMemorySize;
tempProgress = 75;
}

if (tempProgress > 100) {
tempProgress = 99;
}
return tempProgress;
}
}

/// <summary>
/// Returns true when the XML has finished being Deserialized
/// Returns false if it hasn't
/// </summary>
public bool IsCompleted {
get {
if (Result.IsCompleted) {
Xml = DeserializingDelegate.EndInvoke(Result);
return true;
}
else {
return false;
}
}
}

#endregion // Public Properties

/// <summary>
/// Returns a list of all unknown nodes found in the XML in this format
/// Entity Name, First Occurance Line Number, First Occurance Line Position
/// </summary>
/// <returns></returns>
public string GetLog() {
StringBuilder sb = new StringBuilder();
foreach (var item in XmlUnknowns) {
sb.Append(item.Key + ", " + item.Value + Environment.NewLine);
}
return sb.ToString();
}


private LoggedXMLSerializer()
:
base() { // Force Factory Method Use
XmlUnknowns = new Dictionary<string, string>();
}

private LoggedXMLSerializer(string filePath)
:
this() {
DeserializingDelegate = PeformDeserialization;


FileSize = new FileInfo(filePath).Length;
// The XML File tends to take up 110% of space in memory as it does on disc
FileSize = (long)(FileSize * 1.10);

TotalMemoryIncrease = 0;

Result = DeserializingDelegate.BeginInvoke(filePath, null, null);
StartingMemorySize = GetCurrentProcessMemoryInUse();
}

/// <summary>
/// Deserializes the XML on a different thread. Use IsCompleted and Progress to determine status
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
public static LoggedXMLSerializer<T> RunDeserializeAsync(string filePath) {
return new LoggedXMLSerializer<T>(filePath);
}

private T PeformDeserialization(string filePath) {
XmlSerializer xs = new XmlSerializer(typeof(T));
xs.UnknownNode += new XmlNodeEventHandler(Xs_UnknownNode);

StreamReader reader = File.OpenText(filePath);

return (T)xs.Deserialize(reader);
}

private long GetCurrentProcessMemoryInUse() {
Process process = Process.GetCurrentProcess();
return process.WorkingSet64;
}

private void Xs_UnknownNode(object sender, XmlNodeEventArgs e) {
if (!XmlUnknowns.ContainsKey(e.Name)) {
XmlUnknowns.Add(e.Name, e.LineNumber + ", " + e.LinePosition);
}
}
}