I'm writing two APIs that I will use with many of my projects. Some projects my use one of the APIs, some the other, but the majority of my projects will use both. I'm trying to design them as if they're completely separate, but I'm struggling on one thing.
namespace FirstApi {
public abstract class MyBaseClass {
//constructor, some methods and properties
public IEnumerable<T> Search<T>() where T : MyBaseClass, new() {
//search logic here. must use generics as I create new instances of T here
}
}
}
namespace SecondApi {
public interface IMyInterface {
//some property and method signatures
IEnumerable<T> Search<T>() where T : IMyInterface, new();
}
}
namespace MyProject {
public class MyDerivedClass : MyBaseClass, IMyInterface {
}
}
Both APIs require this search method. The second API has some functionality in other classes that calls IMyInterface.Search<T>()
, and I would like those classes that inherit MyBaseClass
to use the Search<T>
function defined in MyBaseClass
.
Compilation error: The constraints for type parameter 'T' of method 'MyBaseClass.Search()' must match the constraints for type parameter 'T' of interface method 'IMyInterface.Search()'. Consider using an explicit interface implementation instead.
Note: When Search is called, T will always be the derived class of whichever abstract class or interface has been inherited. This was the only way I could find of achieving this in C# 2.0 (C# abstract class return derived type enumerator), and it's just caused more problems!
Is there a type-safe way that I can achieve this, without using objects and casting?
Solution:
Based on the accepted answer by Andras Zoltan, I created this class in my project, and will have to re-create this class for each project that uses both APIs.
public abstract class ApiAdapter<TAdapter> : MyBaseClass, IMyInterface where TAdapter: MyBaseClass, IJsonObject, new()
{
IEnumerable<T> IJsonObject.Search<T>()
{
foreach (TAdapter row in base.Search<TAdapter>())
yield return (T)(IMyInterface)row;
}
}
I then inherit this class like so.
public class Client : ApiAdapter<Client> {
//everything else can go here
}
You can explicitly implement the interfaces Search method, e.g.
public class MyDerivedClass : BasicTestApp.FirstApi.MyBaseClass, BasicTestApp.SecondApi.IMyInterface
{
IEnumerable<T> SecondApi.IMyInterface.Search<T>()
{
// do implementation
}
}
However, I think you are asking for the MyBaseClass
Search method to be called when the part of the code that handles your object as IMyInterface
calls the Search<T>
method. I cannot see a way because you have two T
types with different constraints that cannot be related.
If you did where T : BasicTestApp.FirstApi.MyBaseClass, IMyInterface, new();
in both definitions of the Search method then you would not have a problem but this would tie both your APIs together
Here is a possible implementation of your explicitly implemented interface method. It doesn't avoid the cast but at least keeps it neat.
IEnumerable<T> SecondApi.IMyInterface.Search<T>()
{
var results = base.Search<MyDerivedClass>();
return results.Cast<T>();
}
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