Search code examples
c#genericsxmlserializerxelement

Convert Type parameter to T


I'm a little stumped on how to pass in a type argument to a Method, from a parameter.

public class MyNamespaceXmlFormatter : XmlMediaTypeFormatter
{
    public override Task WriteToStreamAsync(Type type, object value, 
                                            Stream writeStream, HttpContent content, 
                                            TransportContext transportContext, System.Threading.CancellationToken cancellationToken)
    {
        XElement xmloutput = XElementSerialiser.ToXElement<type>(value);

        ...........

I've tried the following, but these fail as well;

XElement xmloutput = XElementSerialiser.ToXElement<(T)type>(value);

XElement xmloutput = XElementSerialiser.ToXElement<typeof(type)>(value);

Obviously this compiles, but makes little sense;

XElement xmloutput = XElementSerialiser.ToXElement<Type>(value);

Solution

  • Generic instantiation (i.e. adding <T> to signature) is a compile-time construct, while Type object exists at runtime.

    It would be preferable, as D Stanley suggested, to make the whole method generic and not pass a Type object at all.
    However, if you're unable to do that, you can make yourself a generic factory with non-generic interface, and create instances of it dynamically. Like so:

    interface IFactory {
        XElement ToXElement( object value );
    }
    
    class Factory<T> : IFactory {
        public XElement ToXElement( object value ) { 
            return XElementSerialiser.ToXElement<T>( value ); 
        }
    }
    
    public override Task WriteToStreamAsync( Type type, object value, 
                                        Stream writeStream, HttpContent content, 
                                        TransportContext transportContext, System.Threading.CancellationToken cancellationToken) {
        var factory = Activator.CreateInstance( typeof( Factory<> ).MakeGenericType( type ) ) as IFactory;
        return factory.ToXElement( value );
    }
    

    For performance reasons, you might also want to cache the factory instance.

    UPDATE: made a blog post out of it.