views:

430

answers:

2

Here's the scenario i am faced with:

public abstract class Record { } 

public abstract class TableRecord : Record { } 

public abstract class LookupTableRecord : TableRecord { } 

public sealed class UserRecord : LookupTableRecord { } 

public interface IDataAccessLayer<TRecord> 
    where TRecord : Record { } 

public interface ITableDataAccessLayer<TTableRecord> : IDataAccessLayer<TTableRecord> 
    where TTableRecord : TableRecord { } 

public interface ILookupTableDataAccessLayer<TLookupTableRecord> : ITableDataAccessLayer<TLookupTableRecord> 
    where TLookupTableRecord : LookupTableRecord { } 

public abstract class DataAccessLayer<TRecord> : IDataAccessLayer<TRecord> 
    where TRecord : Record, new() { } 

public abstract class TableDataAccessLayer<TTableRecord> : DataAccessLayer<TTableRecord>, ITableDataAccessLayer<TTableRecord> 
    where TTableRecord : TableRecord, new() { } 

public abstract class LookupTableDataAccessLayer<TLookupTableRecord> : TableDataAccessLayer<TLookupTableRecord>, ILookupTableDataAccessLayer<TLookupTableRecord> 
    where TLookupTableRecord : LookupTableRecord, new() { } 

public sealed class UserDataAccessLayer : LookupTableDataAccessLayer<UserRecord> { }

Now when i try to cast UserDataAccessLayer to it's generic base type ITableDataAccessLayer<TableRecord>, the compiler complains that it cannot implicitly convert the type.

When i try and use the in or out keywords in the interface declaration for the generic parameters, the compiler complains about Invalid variance: The type parameter must be invariantly valid.

I have the following abstract class:

public abstract class FileProcessor : IDisposable
{
    protected abstract ITableDataAccessLayer<TableRecord> CreateTableDataAccessLayer();
}

And a sample concrete implementation as follows:

public class UserFileProcessor : FileProcessor
{
            protected override ITableDataAccessLayer<TableRecord> CreateTableDataAccessLayer()
        {
            return new UserDataAccessLayer();
        }
}

return new UserDataAccessLayer(); is where the compiler is complaining.

A: 

As far as I know you need to specify the out keyword for the interface if you want to use covariance.

http://geekswithblogs.net/NewThingsILearned/archive/2009/09/30/covariance-in-.net-4.0.aspx

Snake
I tried setting out on the generic parameters on the generic interface as well. But then the compiler gave me a different error:Invalid variance: The type parameter 'TTableRecord' must be invariantly valid on...
Sameer Shariff
Please post the full error message.
Snake
If i apply the out keyword to the TTableRecord generic parameter in the generic ITableDataAccessLayer interface declaration, i get the following error:Invalid variance: The type parameter 'TTableRecord' must be contravariantly valid on 'Csss.Data.DataAccessLayers.ITableDataAccessLayer<TTableRecord>.Delete(TTableRecord, bool)'. 'TTableRecord' is covariant.
Sameer Shariff
A: 

The problem with co- and contra-variance is that it imposes quite a bit of restrictions on the types involved, so it might not be applicable in all cases.

I managed to get your code to compile by making the following changes:

public interface IDataAccessLayer<out TRecord>
    where TRecord : Record { }

public interface ITableDataAccessLayer<out TTableRecord> : IDataAccessLayer<TTableRecord>
    where TTableRecord : TableRecord { }

Note:

  • out added for IDataAccessLayer and ITableDataAccessLayer

This, however, means you're not restricted to using TTableRecord only in output positions in those types, which means:

  • type for readonly properties (not for writeable properties)
  • return type for methods
  • out argument-types for methods

You can not use it for:

  • writeable properties
  • ref or non-out/ref parameters to methods

So likely, there is no way to make co- and contra-variance help you here.

Lasse V. Karlsen
Hi LasseDoes not work. Add a method to ITableDataAccessLayer which uses type TTableRecord as a parameter or return type.
Sameer Shariff
Is there anything else i can do to downcast a concrete type to a base generic type?
Sameer Shariff
As I said, it has tons of restrictions, I doubt you can do what you want.
Lasse V. Karlsen