Question

I want to implement same simple generic conversion method but on runtime I am getting an error.

So the scenario is quite simple. I have same service that return me list of items of type External. I have my own WrapperExternal class that simply wrap this class and expose some additional functionality to it. I have some another set of classes that inherit from WrapExternal and add different functionalities.

I want to create generic method that accept list of External list items and return list of items of specified type.

My application code:

    static void Main(string[] args)
    {
        var items = GetItemsFromServer();
        var converted = ConvertItems<SubWrapperExternal>(items).ToList();
    }

    public static IEnumerable<T> ConvertItems<T>(IEnumerable<External> externalItems) where T : WrapperExternal
    {
        return externalItems
            .Where( item => true)
            .Select(item => (T)item);
    }

When you try to run this code you will get exception in line (T)item:

An unhandled exception of type 'System.InvalidCastException' occurred in ConsoleApplication1.exe 
Additional information: 
Unable to cast object of type 'ConsoleApplication1.WrapperExternal' to type 'ConsoleApplication1.SubWrapperExternal'.

Do you know how can I make it to works?

Test application code:

namespace ConsoleApplication1
{
class Program
{
    static void Main(string[] args)
    {
        var items = GetItemsFromServer();

        var converted = ConvertItems<SubWrapperExternal>(items).ToList();

    }

    private static List<External> GetItemsFromServer()
    {
        return new List<External>
        {
            new External{Name = "A"},
            new External{Name = "B"},
            new External{Name = "C"},
        };
    }

    public static IEnumerable<T> ConvertItems<T>(IEnumerable<External> externalItems) where T : WrapperExternal
    {
        return externalItems
            .Where( item => true)
            .Select(item => (T)item);
    }
}

class External
{
    public string Name { get; set; }

}

class WrapperExternal
{
    public External External { get; private set; }

    public WrapperExternal(External external)
    {
        External = external;
    }

    public static explicit operator WrapperExternal(External item)
    {
        return item != null ? new WrapperExternal(item) : null;
    }

    public static implicit operator External(WrapperExternal item)
    {
        return item != null ? item.External : null;
    }
}

class SubWrapperExternal : WrapperExternal
{
    public SubWrapperExternal(External external)
        : base(external)
    {
    }

    public static explicit operator SubWrapperExternal(External item)
    {
        return item != null ? new SubWrapperExternal(item) : null;
    }

    public static implicit operator External(SubWrapperExternal item)
    {
        return item != null ? item.External : null;
    }
}
}
Was it helpful?

Solution

Conversion operators are a faff to use with generics - generics don't support any static operator overloads. Because of this, the (T) cast is performing a non-converting type check (generics need to use the same IL for every possible T, remember) - a basic castclass.

The only "simple" way of doing what you want is to cheat with dynamic:

return externalItems.Select(item => (T)(dynamic)item);

Since the C#-specific dynamic provider knows all the common rules of C#, it knows about conversion operators, and will apply them on-demand. There is a slight performance cost associated with this, but it isn't as bad as it looks at first glance, as the strategy is cached (as IL) once per type - it doesn't perform per-item reflection.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top