简体   繁体   中英

C# Failing Covariant Cast

I'm trying to create something like a generic table factory with a range of implementations. Example below is self explanatory, and the cast in the end isn't working even though both type attributes marked as 'out'. I suppose, the issue is that Table isn't an interface and this breaks the cast. But it shuldn't really be interface and especially not covariant. Any ideas how to cast it properly? Definitely don't want to use reflection to use methods.

interface ITableRow { }

class Table<T> where T : ITableRow { }

interface ITableFactory<out TRow, out TTable>
    where TRow : ITableRow
    where TTable : Table<TRow>
{
    TTable CreateTable();
}

// example implementation:

class SuperRow : ITableRow { }

class SuperTableFactory : ITableFactory<SuperRow, Table<SuperRow>>
{
    public Table<SuperRow> CreateTable() { throw new NotImplementedException(); }
}

// run:

class VarianceTest
{
    public static void Test()
    {
        var factory = Activator.CreateInstance(Type.GetType("Snippets.Var.SuperTableFactory"));

        var casted = (ITableFactory<ITableRow, Table<ITableRow>>) factory; // cast fails
    }
}

Your SuperTableFactory claims to produce instances of Table<SuperRow> . That means, whatever table you get back from it, you can add SuperRows , and read SuperRows .

However, your ITableFactory<ITableRow, Table<ITableRow>> claims that it produces Table<ITableRows> -- tables that people can add ITableRows to and read ITableRows from.

But your actual table factory produces Table<SuperRow> s -- the underlying row storage is SuperRow . You can't let someone put any old ITableRow in there!

That's why your cast is failing.

You can get around this if you get rid of the "people can add rows to tables" bit, and promise that people can only ever read rows. Then it doesn't matter whether a Table 's row storage is actually SuperRow , as people can't try and put other types of row in there.

You do this by making Table<T> covariant. But as you noted, classes can't be covariant, only interfaces. So you'll need an ITable<out T> .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM