Reputation: 3607
I'm trying to use the c# generics to create a generic interface that works as a base to build all my listing services. However I have a problem with the definition.
abstract class ListingParams { }
abstract class ListingDTO<T> where T : ListingItemDTO
{
public List<T> Items{ get; set; }
}
abstract class ListingItemDTO { }
class HListingParams : ListingParams { }
class HListing : ListingDTO<HItem> { }
class HItem : ListingItemDTO { }
interface IListingToolsService<in TFilter, out U, V>
where TFilter : ListingParams
where U : ListingDTO<V> where V : ListingItemDTO
{
int Count(TFilter parameters);
U Get(TFilter parameters);
}
Here starts the problem, due to the Get method returns a generic type, I have to add a third generic parameter to the interface.
If I want to create a concrete implementation of that interface I have to create something like this:
class HListingToolsService : IListingToolsService<ListingParams, HListing, HItem>
{
public int Count(ListingParams parameters) => throw new NotImplementedException();
public HListing Get(ListingParams parameters) => throw new NotImplementedException();
}
However by definition HListing
is not generic, due to it was defined using HItem
.
Is there any way to create that interface with only two parameters in order to don't repeat a type that is already defined?
Upvotes: 2
Views: 608
Reputation: 23228
You can change your code a little bit, convert ListingDTO<T>
to interface and make generic type parameter T
covariant. In this case you also should change Items
type to IEnumerable<T>
, since List<T>
is invariant.
interface IListingDTO<out T> where T : ListingItemDTO
{
public IEnumerable<T> Items { get; }
}
Then implement it in HListing
class
class HListing : IListingDTO<HItem>
{
public IEnumerable<HItem> Items { get; }
}
After that you can update IListingToolsService
interface and get rid of V
generic type parameter and its constraint
interface IListingToolsService<in TFilter, out U>
where TFilter : ListingParams
where U : IListingDTO<ListingItemDTO>
{
int Count(TFilter parameters);
U Get(TFilter parameters);
}
Finally implement HListingToolsService
class
class HListingToolsService : IListingToolsService<ListingParams, HListing>
{
public int Count(ListingParams parameters) => throw new NotImplementedException();
public HListing Get(ListingParams parameters) => throw new NotImplementedException();
}
Covariant declaration of IListingDTO<out T>
allows you to use HListing
( which implements this interface) in service implementation
Upvotes: 1
Reputation: 110
This might change your design a little bit, but I'd try replacing the following code with something that doesn't take type parameter. In this way we can encapsulate ListingDTO into a class that doesn't take type parameter directly.
abstract class ListingItemBase : ListingItemDTO { }
abstract class ListingDTOBase : ListingDTO<ListingItemBase>
{
public List<ListingItemBase> Items { get; set; }
}
And make HListing inherit ListingDTOBase:
class HListing : ListingDTOBase { }
After that you should be able to define interface without using V type parameter:
interface IListingToolsService<in TFilter, out U>
where TFilter : ListingParams
where U : ListingDTOBase
{
int Count(TFilter parameters);
U Get(TFilter parameters);
}
And service:
class HListingToolsService : IListingToolsService<ListingParams, HListing>
{
public int Count(ListingParams parameters) => throw new NotImplementedException();
public HListing Get(ListingParams parameters) => throw new NotImplementedException();
}
Upvotes: 1