Kategorien

Weblog Liste

Archiv

Administration

Sign In
 Tuesday, December 12, 2006
Tuesday, December 12, 2006 6:25:27 PM (Mitteleuropäische Zeit, UTC+01:00) #
Comments [0]

Wie versprochen nun die Fortsetzung.

Um "beliebige" Objekte lesen und schreiben zu können, bediene ich mich der Serialisierungmöglichkeiten von .NET. Es werden zwei neue Methoden eingeführt GetObject<T> und SetObject<T>. Die Implementierung dieser Methoden sieht dann wie folgt aus.

// <summary>
/// Gets the object.
/// </summary>
/// <param name="node">The node.</param>
/// <returns></returns>
private static T GetObject<T>(XmlNode node)
{
T result = default(T);

    //restore object from node
XmlSerializer ser = new XmlSerializer(typeof(T));
result = (T)ser.Deserialize(new StringReader(node.OuterXml));

return result;
}

/// <summary>
/// Sets the object.
/// </summary>
/// <param name="node">The node.</param>
/// <param name="value">The value.</param>
private static void SetObject<T>(XmlNode node, T value)
{
if(value == null)
{
return;
}

using(MemoryStream stream = new MemoryStream())
{
//serialize object
XmlSerializer ser = new XmlSerializer(typeof(T));
ser.Serialize(stream, value);

//reset read position
stream.Position = 0;

XmlElement newNode = null;

using(XmlReader reader = XmlReader.Create(stream))
{
//move to the content node.
reader.MoveToContent();
//create new element in node context
newNode = node.OwnerDocument.CreateElement(reader.Name);
//set content
newNode.InnerXml = reader.ReadInnerXml();
}

//append the new node
node.AppendChild(newNode);
}
}
Die beiden Methoden können natürlich auch public gemacht werden, damit sie explizit aufgerufen werden können. Diese Variante ist im Falle einer reinen Hilfsklasse für unterschiedlichste Projekte in der Tat auch vorzuziehen, da es die Verwendungsmöglichkeiten besser hervorhebt.

Im konkreten Beispiel habe ich sie einfach als zusätzlichen Aufruf in die Methoden SetValue<T> und GetValue<T> eingebaut. Irgendwie muß ich ja mein Versprechen erfüllen, mich von der IConvertible-Schnittstelle zu trennen. Nun noch kurz die geänderten Bereiche obiger Methoden, dann sollte ich meiner Verpflichtung nachgekommen sein. :-)

public static T GetValue<T>(XmlNode node, T @default)
{
    ...
    if(!IsConvertible(typeof(T)))
    {
        //get complex object
        result = GetObject<T>(node);
        result = result != null ? result : @default;
    }
    else
    {
        ...
    }
    ...
}

public static void SetValue<T>(XmlNode node, T value)
{
    ...
    if(!IsConvertible(typeof(T)))
    {
        SetObject<T>(node, value);
        return;
    }
    ...
}

Diese Lösung ist ganz klar ein erster Ansatz. Sie weist in dieser Ausprägung noch einige Lücken auf, welche ich nicht verschweigen möchte.
  • Das Löschen von Objektknoten ist in dieser Variante nicht möglich
  • Das Setzen eines komplexen Objektes führt immer zu einem anfügen. Durch die nicht vorhandene Objektidentität wird bei mehrfachen Aufrufen das Objekt auch mehrfach hinzugefügt.
  • Das unterschiedliche Verhalten von Lesen und Schreiben kann zu Irritationen führen.
  • Die XML-Serialisierung kann unter Umständen nicht erfolgreich sein, was dann einige Anforderungen an die Klassen stellt, um dies dennoch zu ermöglichen.

 Monday, December 11, 2006
Monday, December 11, 2006 9:26:23 AM (Mitteleuropäische Zeit, UTC+01:00) #
Comments [0]

Nachdem mich am Donnerstag ein Virus dahin gerafft hat und ich das Bett hüten durfte, soll es heute wieder was neues geben.


Häufig hat man es mit XML-Daten zu tun, welche ausgelesen und bearbeitet werden sollen. Die enthaltenen Daten sind oft von einem bestimmten Typ. Die nachfolgende Hilfsklasse stellt Schnittstellen zur Verfügung, um die notwendigen Typumwandlungen zu kapseln. Diese Variante funktioniert nur mit Typen welche IConvertible implementieren. Eine Erweiterung, welche diese Einschränkung nicht besitzt, gibt es morgen. :)

using System.Xml;
using System;

namespace de.wwaitz.Utils.XmlHelper
{
public class NodeHelper
{
/// <summary>
/// Gets a value indicating whether T is convertible.
/// </summary>
/// <value>
///     <c>true</c> if T is convertible; otherwise, <c>false</c>.
/// </value>
public static bool IsConvertible(Type resultType)
{
return typeof(IConvertible).IsAssignableFrom(resultType);
}

/// <summary>
/// Gets the value.
/// </summary>
/// <param name="node">The node.</param>
/// <param name="@default">The @default.</param>
/// <returns></returns>
public static T GetValue<T>(XmlNode node, T @default)
{
T result = @default;

if(node == null) {
//invalid node
return result;
}

if(!IsConvertible(typeof(T))) {
//cast is not possible
return result;
}

//get value
string toGet = node.InnerText;
//convert to T
result = (T)Convert.ChangeType(toGet, typeof(T));

return result;
}

/// <summary>
/// Sets the value.
/// </summary>
/// <param name="node">The node.</param>
/// <param name="value">The value.</param>
public static void SetValue<T>(XmlNode node, T value)
{
if(node == null) {
//invalid node
return;
}

if(!IsConvertible(typeof(T))) {
//cast is not possible
return;
}

string toSet = Convert.ToString(value);
node.InnerText = toSet;
}
}
}

Die Verwendung könnte dann wie folgt aussehen:

internal class Program
{
private static void Main(string[] args)
{
string xml = "<root><my-bool>true</my-bool></root>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);

bool myBool = NodeHelper.GetValue<bool>(doc.DocumentElement["my-bool"], false);
NodeHelper.SetValue<bool>(doc.DocumentElement["my-bool"], !myBool);
}
}
 Tuesday, December 05, 2006
Tuesday, December 05, 2006 7:10:02 PM (Mitteleuropäische Zeit, UTC+01:00) #
Comments [1]

Vor einiger Zeit hatte ich mal ein kleines Helferlein geschrieben, um Listen zu sortieren. Gut, das Problem lässt sich eleganter mit delegates lösen aber Spaß hat es trotzdem gemacht. :)

using System;
using System.Collections.Generic;
using System.Reflection;

namespace System.Collections.Generic
{
/// <summary>
/// Ein generischer Algorithmus zum sortieren von Listen.
/// </summary>
public class GenericSort<T> : IComparer<T>
{
private MethodInfo _method;

/// <summary>
/// Erzeugt eine Instanz des Typs
/// [invariant] Der übergebene Member kann eine Methode oder ein Property sein.
/// Handelt es sich um eine Methode, darf sie keinen Parameter
/// besitzen. der Rückgabetyp der Eigenschaft/Methode muß vom Typ
/// IComparable sein.
/// </summary>
/// <param name="member"></param>
public GenericSort(string member)
{
MemberInfo[] infos = typeof (T).GetMember(member);

foreach (MemberInfo info in infos)
{
if (info.MemberType == MemberTypes.Property)
{
if (Initialize((PropertyInfo) info))
break;
}
else if (info.MemberType == MemberTypes.Method)
{
if (Initialize((MethodInfo) info))
break;
}
}

if(_method == null)
throw new InvalidOperationException("Member does not match the class invariant");
}

/// <summary>
/// Initializes the specified info.
/// </summary>
/// <param name="info">The info.</param>
/// <returns></returns>
private bool Initialize(PropertyInfo info)
{
return Initialize(info.GetGetMethod());
}

/// <summary>
/// Initializes the specified info.
/// </summary>
/// <param name="info">The info.</param>
/// <returns></returns>
private bool Initialize(MethodInfo info)
{
if(info.GetParameters().Length > 0)
return false;

if(!typeof(IComparable).IsAssignableFrom(info.ReturnType))
return false;

_method = info;
return true;
}

/// <summary>
/// Compares two objects and returns a value indicating whether one is less than,
/// equal to, or greater than the other.

/// </summary>
/// <param name="x">The first object to compare.</param>
/// <param name="y">The second object to compare.</param>
/// <returns>
/// Value Condition Less than zerox is less than y.Zerox equals y.Greater
/// than zerox is greater than y.

/// </returns>
public int Compare(T x, T y)
{
return ((IComparable) _method.Invoke(x, null)).CompareTo(_method.Invoke(y, null));
}
}
}
Die Sortierung Liste mit Elementen des fiktiven Typs "SomeType" erfolgt dann einfach über nachfolgenden Aufruf:

public class Program
{
    public static void Main(string[] args)
    {
        List<SomeType> list = new List<SomeType>();

        //Code

        list.Sort(new GenericSort<SomeType>("Value"));
    }
}

class SomeType
{
    private int _value;

    public int Value
    {
        get { return _value; }
        set { _value = value; }
    }
}