Reputation: 61
I have the following generic interface:
public interface IContainer<T>
and a generic interface that acts as a container for these generic interfaces:
public interface IGroupOfContainers<TContainer, TValue>
where TContainer : IContainer<TValue>
Now I can define some classes:
public class SomeContainer<T> : IContainer<T>
...
public class OtherContainer<T> : IContainer<T>
...
public class SomeGroupOfContainers<TContainer, TValue>
: IGroupOfContainers<TContainer, TValue>
where TContainer : IContainer<TValue>
...
public class OtherGroupOfContainers<TContainer, TValue>
: IGroupOfContainers<TContainer, TValue>
where TContainer : IContainer<TValue>
...
Now I want IntGroupManager
to be able to work with any object that complies with the IGroupOfContainers<IContainer<int>, int>
interface:
public class IntGroupManager
{
public IGroupOfContainers<IContainer<int>, int> IntGroup { get; set; }
...
}
But when I try:
var manager= new IntGroupManager();
manager.IntGroup = new GroupOfContainers<Container<int>, int>
as IGroupOfContainers<IContainer<int>, int>;
I get a "Suspicious cast: there is no type which is inherited from both GroupOfContainers<Container<int>, int>
and IGroupOfContainers<IContainer<int>, int>
" warning, and when I attempt to run the code, manager.IntGroup
is set to null
, as the cast failed.
If I change the type signature of the IntGroup
property to
public IGroupOfContainers<Container<int>, int> IntGroup { get; set; }
(changing IContainer
to the concrete class Container
), the code executes properly. However, I then wouldn't be able to instantiate an instance of IntGroupManager
with an IntGroup
of type, say, GroupOfContainers<OtherContainer<int>, int>
, since OtherContainer
is not derived from Container
.
I could introduce generic parameters into the definition of IntGroupManager
, perhaps rewriting it as
public class IntGroupManager<TContainer> where TContainer : IContainer<int>
However, this would bind any instance of IntGroupManager
to a specific type of IContainer
; I wouldn't be able to do
var manager = new IntGroupManager<Container<int>>();
// Set manager.IntGroup and do some things with it
// This is okay
manager.IntGroup = new GroupOfContainers<Container<int>, int>();
// manager executes some code involving IntGroup and manipulates its internal state
// Set manager.IntGroup to something else and do things with it
// This is not okay, since OtherContainer<int> is not derived from Container<int>
manager.IntGroup = new GroupOfContainers<OtherContainer<int>, int>();
and so I lose some flexibility.
How can I properly define the IntGroup
property to be of any type conforming to IGroupOfContainers<IContainer<int>, int>
? I want to be able to use the same instance of IntGroupManager
with any IntGroup
object that conforms to IGroupOfContainers<IContainer<int>, int>
. Is there a way to do this?
Upvotes: 1
Views: 166
Reputation: 11559
Though it is hard to say without seeing how you used TContainer
and TValue
(input or output?) but, have you tried using out
keyword for the generic type arguments? For details you can check Covariance and Contravariance
public interface IContainer<out T>
public interface IGroupOfContainers<out TContainer, TValue>
where TContainer : IContainer<TValue>
Upvotes: 1
Reputation: 172468
This is an answer to your question in the comments of Mehmet's answer (sorry, too long for a comment):
If it were either input or output, and not both, [covariance] would be the solution, right? Is there any way to do this even if IGroupOfContainers defined functions that used TContainer as both input and output?
Let's continue with your example and assume that the following statement works:
var g = new GroupOfContainers<Container<int>, int>
as IGroupOfContainers<IContainer<int>, int>;
Let's assume that IGroupOfContainers<TContainer, TValue>
has a method AddContainer(TContainer)
:
g.AddContainer(new OtherContainer<int>());
This would compile, since OtherContainer<int>
is an IContainer<int>
.
However, you just added an OtherContainer
to a group of Container
s! That's why you cannot do what you want to do unless you prevent IGroupOfContainers
from taking TContainer
inputs.
Upvotes: 2