Cannot implicitly convert derived type

Steven Schveighoffer schveiguy at yahoo.com
Fri Feb 21 17:03:22 PST 2014


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


More information about the Digitalmars-d-learn mailing list