【问题标题】:Overriding properties & inheritance issue覆盖属性和继承问题
【发布时间】:2015-03-26 08:04:25
【问题描述】:

我没有对我的任何模块进行重大更改,但突然间编译我的很多模块时开始失败。

我收到以下错误

borders.d(190): Error: function aeri.context.Context.position (Point newPosition) is not callable using argument types ()
borders.d(191): Error: function aeri.context.Context.size (Point newSize) is not callable using argument types ()
controls\image.d(117): Error: function aeri.context.Context.size (Point newSize) is not callable using argument types ()
controls\image.d(121): Error: function aeri.context.Context.size (Point newSize) is not callable using argument types ()
controls\image.d(121): Error: function aeri.context.Context.size (Point newSize) is not callable using argument types ()
controls\imagebutton.d(45): Error: function aeri.context.Context.size (Point newSize) is not callable using argument types ()
controls\imagebutton.d(59): Error: function aeri.context.Context.size (Point newSize) is not callable using argument types ()
controls\imagebutton.d(73): Error: function aeri.context.Context.size (Point newSize) is not callable using argument types ()
controls\imagebutton.d(89): Error: function aeri.context.Context.size (Point newSize) is not callable using argument types ()
controls\imagebutton.d(100): Error: function aeri.context.Context.size (Point newSize) is not callable using argument types ()
controls\imagebutton.d(110): Error: function aeri.context.Context.position (Point newPosition) is not callable using argument types ()
controls\labelbutton.d(78): Error: function aeri.context.Context.size (Point newSize) is not callable using argument types ()
controls\labelbutton.d(89): Error: function aeri.context.Context.size (Point newSize) is not callable using argument types ()
controls\textbox.d(245): Error: function aeri.context.Context.position (Point newPosition) is not callable using argument types ()
controls\textbox.d(247): Error: function aeri.context.Context.position (Point newPosition) is not callable using argument types ()
controls\textbox.d(259): Error: function aeri.context.Context.position (Point newPosition) is not callable using argument types ()
controls\textbox.d(309): Error: function aeri.context.Context.size (Point newSize) is not callable using argument types ()
controls\textbox.d(310): Error: function aeri.context.Context.position (Point newPosition) is not callable using argument types ()
controls\textbox.d(324): Error: function aeri.context.Context.size (Point newSize) is not callable using argument types ()
controls\textbox.d(325): Error: function aeri.context.Context.position (Point newPosition) is not callable using argument types ()

只是为了展示导致它的原因的示例。这是来自borders.d的代码sn-p

this(Context parent, Paint topPaint, Paint rightPaint, Paint bottomPaint, Paint leftPaint) {
    super();

    if (!parent)
        throw new BorderException("Pass a context to render the border around.");

    m_parent = parent;
    m_topPaint = topPaint;
    m_rightPaint = rightPaint;
    m_bottomPaint = bottomPaint;
    m_leftPaint = leftPaint;

    position = parent.position; // Line 190
    size = parent.size; // Line 191
}

现在让我们看看Context.positionContext.size

override void size(Point newSize) {
        super.size = newSize;

        if (m_backgroundPaint != transparent) {
            m_backgroundShape = new RectangleShape(Vector2f(cast(float)super.width, cast(float)super.height));
            m_backgroundShape.fillColor = m_backgroundPaint.toSfmlColor();
            m_backgroundShape.position = Vector2f(cast(float)super.x, cast(float)super.y);
        }
        if (m_border) {
            m_border.size = Point(super.size);
        }
    }

    override void position(Point newPosition) {
        super.position = newPosition;

        if (m_backgroundShape) {
            m_backgroundShape.position = Vector2f(cast(float)super.x, cast(float)super.y);
        }
        if (m_border) {
            m_border.position = Point(super.position);
        }
    }

那些是二传手。 Context 继承了一个名为 Space 的类,该类具有用于位置和大小的 getter / setter。 Context 只覆盖了 setter,但 getter 是相同的,应该从 Space 调用。似乎它甚至根本没有从Space 获得属性。正如您所看到的,它在应该获取 getter 时尝试获取 setter。然而,所有这些代码都更早地工作了。

这是Space中的属性。

    Point position() { return m_position; }

    void position(Point newPos) {
        m_position = newPos;
    }

    Point size() { return m_size; }

    void size(Point newSize) {
        m_size = newSize;
    }

注意:我将我的所有属性都包装如下,以防您想知道为什么找不到 @property

@property {
    // properties
}

我真的不知道是什么原因造成的,这让我认为这与编译器分析模块的顺序有关?

我仍在尝试自己解决此问题,但我无法发现问题所在,并且在我更改了 Space 中的一些行之后,一切似乎都崩溃了,但没有一个成员。

如果您想知道image.dimagebutton.dlabelbutton.dtextbox.d 中的错误是由同一件事引起的。它们都继承 Context 并覆盖大小/位置。 所以继承基本上是这样的:

x (ex. Image, ImageButton, LabelButton, TextBox etc.)
----Control
--------Context
------------Space

LabelButtonImageButton 继承 Button 继承 ControlControl 不会覆盖任何来自Context 的成员,因此所有对Control 的调用都直接转到ContextButton 也是如此,除了它有一些用于处理鼠标事件的内部成员(在 Context 中处理,但它调用它。)

这里是Control 类和Button 类以防万一。

class Control : Context {
protected:
    this(string name) {
        super(name);
    }
}

...

class Button : Control {
private:
    bool m_intersecting = false;
    bool m_focused = false;

    void handleMouseReleased(Context context, Mouse.Button button) {
        m_focused = m_intersecting;
        if (m_focused && button == Mouse.Button.Left) {
            foreach (event; m_onClick) {
                if (event)
                    event.exec();
            }
        }
    }

    void handleMouseMove(Context context, Point position){
        m_intersecting = super.intersect(position);
    }
protected:
    this(string name) {
        super(name);

        super.onMouseReleased(new MouseClickEvent(&handleMouseReleased));
        super.onMouseMove(new PositionEvent(&handleMouseMove));
    }

    private Action[] m_onClick;

    public void onClick(Action event) {
        if (!validMessageThread)
            throw new MessageException("Cannot register an event outside of the application thread / render thread");

        m_onClick ~= event;
    }
}

如果您需要更多信息或代码,请告诉我。但是我认为这应该足够了。属性/属性调用的继承和覆盖之前工作得很好。

【问题讨论】:

  • 不确定这是否是原因,但您是否更新到新的编译器? (dmd 2067.0)。如果是这样,可能值得降级到 2066.1 看看它是否能修复它。
  • 如果您使用git,您可以使用git blame 查找编译开始失败的点。如果您可以将其本地化为一个小的更改,那么了解和解决问题会容易得多。
  • 在函数声明中直接添加@property是否有效?您可以尝试使用 -property 运行 DMD,它强制执行 @propertyo.prop = x 语法,也许这会提供更好的消息。
  • 还要确保这些 getter 不是私有的,我认为最近关于私有方法的查找方式发生了变化,这可能是造成这种情况的原因。
  • 我的编译器不是最新的。事实上,自去年以来我还没有更新我的编译器。这一切都在早些时候起作用,然后突然发生了这种情况。我也没有使用 git。使用 -property 编译也不起作用。手动声明@property 也没有改变任何东西。最后,它们都在类的公共范围内声明。我发现了它的原因,它似乎是一个编译器错误。显然这是因为 Context 覆盖了位置/大小(仅设置器),然后它永远不会在空间中向下查找 getter。如果我在上下文中注释掉位置/大小,那么它会起作用。

标签: inheritance properties d overriding


【解决方案1】:

解决了这个问题,它似乎是一个编译器错误(或者至少它似乎不是一个正常的行为。)

我在Context 中从Space 覆盖position/size 的setter,因此编译器从未在Space 中寻找getter,而是在Context 中寻找。

我为解决这个问题所做的就是在 Context 中从 Space 覆盖 position/size 的吸气剂。

【讨论】:

  • 这不是编译器错误。规范说,在进行重载解析时,不考虑基类中的函数。要考虑它们,您可以使用如下别名:alias position = Space.position;。来源:dlang.org/function.html#function-inheritance
  • 我明白了,感谢您的澄清 :) 老实说,我认为这是一种奇怪的行为,但我想这背后是有原因的 :p