Matt Austin
Matt Austin

Reputation: 249

C# Generic Method Declaration

I have a problem with the signature of generic method that I am trying to create.

The class is as follows:

public class MarketDataRepository : IRepository
{
    MarketWatchEntities _dbContext = new MarketWatchEntities();

    public MarketDataRepository(){}

    public MarketDataRepository(MarketWatchEntities dbContext)
    {
        _dbContext = dbContext;
    }

    public IEnumerable<T> GetMarketData<T>(DateTime startDateTime, DateTime endDateTime ) where T : MarketDataPoint
    {
        return _dbContext.MarketDataPoints.Where(x => x.ReferenceDateTime >= startDateTime && x.ReferenceDateTime <= endDateTime).ToList();
    }
}

public interface IRepository
{
    IEnumerable<T> GetMarketData<T>(DateTime startDateTime, DateTime endDateTime) where T : MarketDataPoint;
}

Additional information:
MarketWatchEntities extends DbContext (generated by Entity Framework).

I get this compile time exception:

Cannot implicitly convert type 'System.Collections.Generic.List<MarketWatcher.Domain.MarketDataPoint>' to 'System.Collections.Generic.IEnumerable<T>'. An explicit conversion exists (are you missing a cast?)

I was expecting this to be OK on the basis that I'm constraining T to be inherited from MarketDataPoint. So, I thought I would be able to return an IEnumerable of MarketDataPoint.

Any pointers as to what is wrong with the above and what I should do instead would be hugely appreciated!

Solution (Thanks Everyone):

public class MarketDataMarketDataRepository : IMarketDataRepository
{
    MarketWatchEntities _dbContext = new MarketWatchEntities();

    public MarketDataMarketDataRepository(){}

    public MarketDataMarketDataRepository(MarketWatchEntities dbContext)
    {
        _dbContext = dbContext;
    }

    public IQueryable<T> GetMarketData<T>(DateTime startDateTime, DateTime endDateTime ) where T : MarketDataPoint
    {
        return _dbContext.MarketDataPoints.Where(x => x.ReferenceDateTime >= startDateTime && x.ReferenceDateTime <= endDateTime).OfType<T>();
    }
}

Upvotes: 0

Views: 730

Answers (2)

Rawling
Rawling

Reputation: 50114

The reason this doesn't work is because T can be more specific than MarketDataPoint.

If you have class S that derives from MarketDataPoint and someone calls GetMarketData<S>, how can you give them an IEnumerable<S> when all you have is a collection of MarketDataPoints?

EDIT: As Matt himself points out, you can chain a call to OfType<T>() that will weed out the non-T elements and return the correct type of IEnumerable.

return _dbContext.MarketDataPoints
    .Where(
        x => x.ReferenceDateTime >= startDateTime &&
        x.ReferenceDateTime <= endDateTime)
    .OfType<T>()
    .ToList();

Upvotes: 5

LightStriker
LightStriker

Reputation: 21004

And if you follow the error and manually cast it IEnumerable<T>?

public IEnumerable<T> GetMarketData<T>(DateTime startDateTime, DateTime endDateTime) where T : MarketDataPoint
{
    return (IEnumerable<T>)_dbContext.MarketDataPoints.Where(x => x.ReferenceDateTime >= startDateTime && x.ReferenceDateTime <= endDateTime).ToList();
}

The problem is that IEnumerable<MarketDataPoint> is not the same as IEnumerable<T>.

Upvotes: 0

Related Questions