I'm attempting to create a common interface which will allow me n methods of interacting with a database. I want my business application to be able to instantiate any of the connection methodologies and be assured the interface is identical.
Here's a simplified version of what I'm trying now.
Database Interface where IElement
is another interface which would define a table.
public interface IDatabase
{
void setItem( IElement task ); //this works fine
List<T> listTasks<T>() where T : IElement; // this doesn't
}
IElement interface:
public interface IElement
{
int id { get; set; }
}
Implementation of IElement:
public class TaskElement: IElement
{
public int id { get; set; }
public string name {get; set; }
}
Implementation of IDatabase:
public class SQLiteDb: IDatabase
{
public SqLiteDb( SQLiteConnection conn )
{
database = conn;
}
public void setItem( IElement task )
{
// works fine when passed a new TaskElement() which is an implementation of IElement.
database.Insert( task );
}
//it all goes off the rails here
public List<T> listItems<T>() where T : IElement
{
var returnList = new List<IElement>
foreach (var s in database.Table<TaskElement>())
{ returnList.Add(s); }
return returnList;
}
I've tried a lot of variations on this but each one gives me a new issue. Here, for instance, there are two errors.
1)
The type arguments for method
'SQLiteDb.listTasks<T>()'
cannot be inferred from the usage. Try specifying the type arguments explicitly.
2)
Cannot implicitly convert type
'System.Collections.Generic.List<TaskElement>'
to'System.Collections.Generic.List<T>'
I've tried changing the method to use an explicit type but have had issues there. If I use IElement
(my generic interface for all elements )I can't return a list of TaskElement objects (my implementation of IElement
) as it doesn't match the return type (List<IElement>
) and if I change the return type to List<TaskElement>
I'm not longer implementing the interface.
It's worth noting that I can easily get this to work if I stop using the interface and generics, but this seems like an ideal situation to use an interface. Maybe I'm trying to hard to cram a lot of stuff into an interface when another application (like direct inheritance) might be better?
Question
How can I implement an interface with a generic return value while limiting the types which can be returned to only implementations of another interface.
Let's look closely at your implementation of listItems
:
public List<T> listItems<T>() where T : IElement
{
var returnList = new List<IElement>
foreach (var s in database.Table<TaskElement>())
{ returnList.Add(s); }
return returnList;
}
What you've done here is written a method where the caller is allowed to ask for any type they want in the list as long as that type implements IElement
. But the code in your method doesn't give them a list of the type they want, it gives them a list of IElement
. So it's violating the contract.
But the real root of your problem is database.Table<TaskElement>()
. That can only ever give you instances of TaskElement
. You need to make that T
, but to do that you need an additional generic constraint:
public List<T> listItems<T>() where T : IElement, new
{
var returnList = new List<T>
foreach (var s in database.Table<T>())
{
returnList.Add(s);
}
return returnList;
}
This is because database.Table<T>
has a new
constraint, which means that it can only be given types that have a zero-parameter constructor (because that method is going to create instances of the given class).
I belive it should be something like this
public List<T> listItems<T>() where T : IElement
{
var returnList = new List<T>
foreach (var s in database.Table<T>())
{ returnList.Add(s); }
return returnList;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With