Cannot implicitly convert derived type
Frustrated
c1514843 at drdrb.com
Sat Feb 22 12:17:37 PST 2014
On Saturday, 22 February 2014 at 01:03:22 UTC, Steven
Schveighoffer wrote:
> On Fri, 21 Feb 2014 17:54:06 -0500, Frustrated
> <c1514843 at drdrb.com> wrote:
>
>> interface iGui
>> {
>> @property iButton button(ref iButton button);
>> }
>>
>> class WindowsGui : iGui
>> {
>> WindowsButton _button;
>>
>> @property WindowsButton button(ref WindowsButton button)
>> //@property iButton button(ref iButton button)
>> {
>> _button = button;
>> return button;
>> }
>> }
>>
>> interface iButton { }
>> class WindowsButton : iButton { }
>>
>>
>> Should this not work?
>
> What you are trying to do is not legal.
>
> e.g.:
>
> class RogueButton : iButton { }
>
> iGui gui = new WindowsGui;
> gui.button = new RogueButton;
>
> Note that setting gui.button to any iButton is legal, but the
> derived type REQUIRES a WindowsButton. This would have to be
> rejected at runtime, because the compile-time type is
> implicitly convertible.
>
> There are two types of variance that are logical,
> contravariance and covariance. covariance allows you to
> *return* a more derived type than the base. In other words,
> this would be legal (assume same iButton/WindowsButton
> structure):
>
> interface iGui
> {
> @property iButton button();
> }
>
> class WindowsGui : iGui
> {
> @property WindowsButton button() {...};
> }
>
> This works, because whenever you return a WindowsButton, you
> ALSO are returning an iButton. In fact, D supports this.
>
> The opposite is contravariance, and that's used on *input*
> parameters. In this case, the derived method can accept a base
> of the parameter that the base class defines:
>
> interface iGui
> {
> void button(WindowsButton); // please humor me, I know you
> don't want to do this :)
> }
>
> class WindowsGui : iGui
> {
> void button(iButton);
> }
>
> This is logically sound, because the actual implementation only
> requires an iButton. Therefore, passing a WindowsButton into
> the iGui interface still satisfies that requirement.
>
> However, D does NOT support contravariance.
>
>> 2. In the second case, I can cast to make everything work. This
>> seems wrong. Hence goto 1. WindowsGui is designed to only work
>> with WindowsButton, say, and I should never have to use iButton
>> in the WindowsGui class unless, maybe, I want to support
>> non-windows buttons in the WindowsGui for some reason.
>
> This is actually the correct mechanism if you want to use
> polymorphism. However, in some cases, a templated system may be
> more advantageous than an interface system.
>
> One other possibility is to use overloading -- i.e.:
>
> class WindowsGui
> {
> @property WindowsButton button(WindowsButton b) { return
> _button = b;}
>
> @property WindowsButton button(iButton b)
> {
> if(auto wb = cast(WindowsButton)b)
> button = wb;
> else
> throw new ButtonException;
> }
> }
>
> This is not really an attractive solution, but it could be
> easily generated as a mixed-in solution.
>
> -Steve
It is legal exactly because I will always guarantee that the
proper button will be used.
It is not logically legal as mentioned several times... no one
needs to mention that. But it is legal within the context of the
code. I'll never use a RogueButton so why force me to code for
the chance I might?
Again,
WindowsGui only uses WindowsButton which is a iButton type. So
why force me to always use iButton and cast it to WindowsButton?
Why can't I relax the condition to use the base type?
The only reason it is illegal is because I could use a
RogueButton, BUT I WON'T! If I do, then it is an error.
Basically, the point is, the compiler could enforce the above but
make the code more readable.
e.g.,
I do this:
@property WindowsButton button(WindowsButton b)
{
}
The compiler turns this into
@property WindowsButton button(iButton _b)
{
if (is(_b : WindowsButton)) assert(0, "Rogue button used");
auto b = cast(WindowsButton)_b;
}
One is very clean, the other is not. If by chance I use the wrong
button(a Rogue button) then it results in an error(hopefully user
controlled).
One allows me to program in a natural way while the other makes
me jump through hoops which litters the code with a bunch of
casts and checks which are only there in the odd event that I
assign the wrong type(which, I'm not going to do on purpose).
Again, the whole point of why it is illegal because you can pass
a RogueButton... BUT I DON'T INTEND TO PASS THEM! If I could
absolutely guarantee that I won't pass them then there should be
no problem(no asserts). Since I can't guarantee it I have to
litter my code with checks? The compiler could do this for me.
More information about the Digitalmars-d-learn
mailing list