views:

132

answers:

4

Say you have a group of objects you're creating to handle some XML parsing and all of them take the exact same object, XElement ... as such

public class User
{
    public User(XElement xmlElement)
    {
          Id = xmlElement.GetElementValue("UserId"); 
    }

    public string Id { get; set; }
}

What I would like to do is a method kinda like this ..

public static T ToParsedObject<T>(this XElement xmlElement) where T : new()
{
    return new T(xmlElement);
}

I don't think it's possible to do a static (extension method) like this, but I would like to make this a single method I can re-use. I'm tired of writing ones like ...

public static User ToUser(this XElement xmlElement)
{
    return new User(xmlElement);
}

Any ideas or guidance?

+7  A: 

Unfortunately there's no way to provide a constructor call like this.

Two potential options:

  • Make the type mutable (ick) and make it implement an interface, e.g. IParseableFromXElement. Then you could write:

    public static T ToParsedObject<T>(this XElement xmlElement) 
        where T : new(), IParseableFromXElement
    {
        T ret = new T();
        ret.Parse(xmlElement);
        return ret;
    }
    
  • Have a dictionary from type to delegate constructing the type:

    static Dictionary<Type, Func<XElement, object>> factories = 
        new Dictionary<Type, Func<XElement, object>> 
    {
        { typeof(User), x => new User(x) },
        ...
    };
    
    
    public static T ToParsedObject<T>(this XElement xmlElement) 
    {
        Func<XElement, object> factory = factories[typeof(T)];
        return (T) factory(xmlElement);
    }
    

Both are somewhat hacky, but they'll work...

Jon Skeet
+1, you typed a lot of good stuff in 4 minutes!
Abel
+3  A: 

You can pull this off with reflection:

public static class XElementExtensions
{
    public static T To<T>(this XElement el)
    {
        //var ctor = typeof(T).GetConstructor(new[] { typeof(XElement) });
        //if (ctor == null) /* do something */

        return (T)Activator.CreateInstance(typeof(T), new[] { el });
    }
}

You don't need the constructor check in there, but it would be necessary if you wanted to take special action, like returning default(T).

You would use this method like this:

User u = xmlElement.To<User>();

I do wonder, though, what the benefit of this is over simply calling the constructor on your objects:

User u = new User(xmlElement);

Heck, it's one less character! :)

Matt Hamilton
Indeed it is one less. Perhaps we misunderstood the question? Wasn't the generic code supposed to deal with the same task now taken on by the constructor?
Abel
See above, its not gaining just for one class, its ( ...counts) 13 right now and there's probably another 10-15 to go. Reusability ftw!
jeriley
I ended up using this one -- copy/paste, added in a throw new SomeException() and done and done. I'm now reworking some tests to use xmlElement.To<ClassUnderTest>() ...so I can cheat :-)Thanks for the help
jeriley
+1  A: 

How about reflection?

public static T ToParsedObject<T>(this XElement xmlElement) 
    where T : new()
{
    return (T) Activator.CreateInstance(typeof(T), xmlElement);
}
mfeingold
After I went home for the evening I'd though about that ...thats not a bad approach. Wonder how "expensive" it is to do this?
jeriley
Expensive in terms of performance? I did not research it, but I do not think it will be that much overhead vs. the direct call to a constructor. After all whatever the compiler generates for new should not be very different from what's happening here
mfeingold
A: 

As Matt pointed out, this is not really saving you any typing. You still need to write the conversion code in the classes. But perhaps there's some misunderstanding about your question. Isn't it that you actually want to remove the constructor and create a single method extension to XElement that takes the same syntax each time again? Something like this?

/// <summary>
/// Try to parse an XElement into a matching type. Type must have an Id
/// </summary>
/// <returns>newly created type with Id and other properties filled</returns>
public static T ToParsedObject<T>(this XElement xmlElement)
    where T : new()
{
    T item = new T();
    Type type = typeof(T);

    // you can use GetProperties to go through all of them dynamically
    PropertyInfo prop = type.GetProperty("Id");
    prop.SetValue(item, xmlElement.Element(
        XName.Get(type.Name + "Id")),         // creates UserId or CustomerId etc
        null);
    return item;
}

Depending on how well you can rely on the correct naming of the XML elements, you can use this approach to take away the burden of creating individual type conversion code. You can even go through each property and find a matching XML element name and value for it, dynamically. Write once, use everywhere!

Abel