I'm trying to make all these mapper classes have a common base:
// base
class BaseInput { public string BaseInputValue { get; set; } }
class BaseOutput { public string BaseOutputValue { get; set; } }
interface IMapper<InputType, out OutputType>
where InputType : BaseInput
where OutputType : BaseOutput
{
OutputType Map(InputType input);
}
// example implementation
class Input : BaseInput { public string InputValue { get; set; } }
class Output : BaseOutput { public string OutputValue { get; set; } }
class MyMapper : IMapper<Input, Output>
{
public Output Map(Input input)
{
// custom mapping done here to Output
return new Output { /* mapping */ };
}
}
This code to create a new one of these mappers and assign it to the base compiles fine:
var myBaseMapper = (IMapper<BaseInput, BaseOutput>) new MyMapper();
But I get a runtime error:
Unable to cast object of type 'MyMapper' to type 'IMapper`2[UserQuery+BaseInput,UserQuery+BaseOutput]'.
If I reduce IMapper
to IMapper<out OutputType>
it works fine, but then that requires doing a cast in MyMapper.Map
, which is slightly annoying to have to do in each mapper class. Additionally, that makes me lose the information for deciding which Mapper to use for what BaseInput
and so I have to define that elsewhere.
Is doing this just not possible in C# or is there a way of doing something similar? If not, I will have to rethink my design.
You can't do this because it doesn't make sense, your interface is contravariant in the InputType
parameter (or, rather, it would be if you added the in
keyword to that parameter). To see why this doesn't make sense, let's look at your example:
You want an implementation of IMapper<Input, Output>
to be assignable to IMapper<BaseInput, BaseOutput>
. Let's say we create some new child class of BaseInput
, we'll call it MoreInput
:
class MoreInput : BaseInput
{
public string LotsOfInput { get; set; }
}
Okay, now let's say we have a method whose body looks something like this (and that what you wanted actually worked):
IMapper<BaseInput, BaseOutput> mapper = new MyMapper();
mapper.Map( new MoreInput() );
Well, at this point nothing's wrong: IMapper<BaseInput, BaseOutput>
has a Map
method that accepts BaseInput
as its parameter and MoreInput
is a BaseInput
, so our call to Map
is valid.
Except it's not, because the Map
method we're really calling expects an Input
as its argument and MoreInput
is not Input
. We've broken the type system.
This is what the compiler is telling you when it doesn't let you make that assignment implicitly: that conversion isn't safe. The compiler can't guarantee that the types you expect are the types you get.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.