Reputation: 3085
I would like to use a generic type within another generic type.
but getting the following exception:
The type
'MockManagers.ToPos'
cannot be used as type parameter'Res'
in the generic type or method'MockManagers.IMockManager<Req,Res>'
. There is no implicit reference conversion from'MockManagers.ToPos'
to'MockManagers.MockResponseObject<System.IComparable>'
.
I tried few option to solve this without any luck. This reference suggest to pass the inner generic type to as a parameter to the higher level generic type. is this the only solution?
The code:
My Interface:
/// <summary>
/// Represents IMockManager for mock object creation object.
/// </summary>
/// <typeparam name="Req">The request object Type.</typeparam>
/// <typeparam name="Res">The response object type.</typeparam>
public interface IMockManager<Req, Res>
where Req : MockRequestObject
where Res : MockResponseObject<IComparable>
{...//interface definitions}
objects implementatation:
/// <summary>
/// Represents Mock response for a request object
/// </summary>
public class ToPos : MockResponseObject<byte[]>
{... //implementation}
public class MockBaseObject
{
public int Id { get; set; }
}
public class MockResponseObject<T> : MockBaseObject
{
/// <summary>
/// The response content.
/// </summary>
public T Content { get; set; }
/// <summary>
/// Res creation date.
/// </summary>
public DateTime CreationDate { get; set; }
}
Upvotes: 3
Views: 831
Reputation: 66573
You could just declare ToPos
as deriving from MockResponseObject<IComparable>
instead of MockResponseObject<byte[]>
, but then hide the Content
property with a new one of type byte[]
. However, for this to work you need something that wraps a byte[]
and implements IComparable
. For example:
public class ByteArrayComparable : IComparable
{
public byte[] Data { get; private set; }
public ByteArrayComparable(byte[] data) { Data = data; }
// Implement IComparable.CompareTo() method here!
public int CompareTo(object obj) { ... }
}
public class ToPos : MockResponseObject<IComparable>
{
public new byte[] Content
{
get
{
if (base.Content == null)
return null;
return ((ByteArrayComparable) base.Content).Data;
}
set
{
base.Content = value == null ? null : new ByteArrayComparable(value);
}
}
}
Upvotes: 2
Reputation: 61402
The issue here is that you want generic variance on a class. C# only supports generic variance on interfaces.
Here's a simplified version of what you seem to want to work:
// Works fine: "string" is compatible with "IComparable"
IComparable a = new string('a', 5);
// Error: "List<string>" is not compatible with List<IComparable>"
List<IComparable> b = new List<string>();
This is only possible for interfaces, however, and then only if they satisfy certain variance constraints. One such interface is the IEnumerable<out T>
interface:
// Works fine: IEnumerable is covariant
IEnumerable<IComparable> c = new List<string>();
// Similarly, IEnumerable<string> is compatible with IEnumerable<IComparable>:
IEnumerable<string> d = null;
IEnumerable<IComparable> e = d;
So how do you fix this? Here's one idea.
First, you can't use byte[]
since it's not IComparable
. Let's use string
as an example, but you'll have to find something else suitable that implements this interface.
Second, make MockResponseObject
an interface. Moreover, make it covariant in T
:
public interface IMockResponseObject<out T>
{
T Content { get; }
DateTime CreationDate { get; set; }
}
In order for this to work, Content
can't be settable through the interface.
Last, update the rest of the code to use this interface:
interface IMockManager<Req, Res>
where Req : MockRequestObject
where Res : IMockResponseObject<IComparable>
{
}
public class ToPos : MockBaseObject, IMockResponseObject<string>
{
public string Content
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public DateTime CreationDate
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
}
Observe that ToPos
still has a Content
with a setter: you can set content as long as it isn't through the interface.
With all these changes, the following is now valid and compiles fine:
static IMockManager<MockRequestObject, ToPos> manager = null;
Upvotes: 3