4

I'm trying to create a generic to simplify my codes (it's a web api project), but at somehow it's ended up becoming more complicated than I expected. What I'm trying to implement is something like this:

My First Idea

To simplify my whole real code, this is what I've written:

public interface IDatabaseTable { }

public class ReceiptIndex: IDatabaseTable { }

public interface IBackend<T> where T: IDatabaseTable { }

public class Receipts : IBackend<ReceiptIndex> { }

public class Generic<T> : SyncTwoWayXI, IBackend<T> where T:IDatabaseTable { }

public class BaseController<T> : ApiController where T: IBackend<IDatabaseTable>, new () { }

All of the line above created separately in its own file.

When I try to create controller that Inherit from BaseController

public class ReceiptsBaseController : BaseController<Receipts>

I get an error said

The type 'Receipts' cannot be used as type parameter 'T' in the generic type or method 'BaseController'. There is no implicit reference conversion from 'Receipts' to 'IBackend'.

I try to find a similar problem and end up with something called Covariance and Contravariance problem. Can anyone give feedback for what I'm trying to do or maybe something that can I do to simplify it.

Will Vousden
  • 32,488
  • 9
  • 84
  • 95
Martin Valentino
  • 1,002
  • 2
  • 11
  • 26

4 Answers4

4

The easiest way to solve this, without using covariance and contravariance, which has some important implications, is this:

public class BaseController<TBackend, TDatabaseTable>
    : ApiController
    where TBackend : IBackend<TDatabaseTable>, new() 
    where TDatabaseTable: IDatabaseTable
{ }

And use it in this way

public class ReceiptsBaseController : BaseController<Receipts, ReceiptIndex>
{
}

The syntax is not so compact, but it works like a charm, without the extra implications of covariance or contravariance.

JotaBe
  • 38,030
  • 8
  • 98
  • 117
1

You can try to specify the T in IBackend. Like this:

public class BaseController<T, TBackEndSubType> : ApiController
    where T : IBackend<TBackEndSubType>, new()
    where TBackEndSubType : IDatabaseTable { }

public class ReceiptsBaseController : BaseController<Receipts, ReceiptIndex> { }
Maarten
  • 22,527
  • 3
  • 47
  • 68
  • I try this solution; it solves the error problem. But at somehow when I create a controller inherited ```BaseController``` class, it isn't registered as valid API endpoint. – Martin Valentino May 12 '15 at 15:41
0

On the BaseController you have this condition:

where T: IBackend<IDatabaseTable>

but receipts inhertits IBackend<ReceiptIndex>, which is not directly compatible with IBackend<IDatabaseTable>. You could add 2 generic parameters on your BaseController:

public class BaseController<TBackend, TDatabaseTable> : ApiController 
    where TDatabaseTable: IDatabaseTable 
    where TBackend: IBackend<TDatabaseTable>, new () { }

then you can declare your controller like this:

public class ReceiptsBaseController : BaseController<Receipts, ReceiptIndex>
Stefan
  • 121
  • 7
0

using the out modifier: https://msdn.microsoft.com/en-us/library/dd469487.aspx

Change the IBackend interface to look like this:

 public interface IBackend<out T> where T : IDatabaseTable { }
mageos
  • 1,216
  • 7
  • 15
  • I have tried it, but it turns out make one of method declaration on my interface error. `Invalid variance: The type parameter 'T' must be invariantly valid on 'Backend.Objects.IBackend.GetItem(string, long)'. 'T' is covariant. ` – Martin Valentino May 12 '15 at 09:22
  • Take a look at this answer: http://stackoverflow.com/questions/12484293/invalid-variance-the-type-parameter-t-must-be-invariantly-valid-on-xxx-iitem – mageos May 12 '15 at 09:28
  • The link you mention above suggest that I should remove the ```setter``` on the return object properties, that's kind of impossible for my requirement, because I still need a ```setter``` on my method return object – Martin Valentino May 12 '15 at 15:09
  • @MartinDavidValentinoSiagian, The link I sent was saying that you need to apply the out modifier to other interfacesas well and not just in the IBackend<> interface. – mageos May 12 '15 at 16:20