有趣的铸造和继承

时间:2010-05-25 21:20:00

标签: c# c++ delphi inheritance casting

注意:这个问题是用C#编写的伪代码,但我真的要问哪些语言有解决方案。请不要挂断语法。

说我有两个班级:

 class AngleLabel: CustomLabel
 {
     public bool Bold;  // Just upping the visibility to public
     // code to allow the label to be on an angle
 }

 class Label: CustomLabel
 {
     public bool Bold;  // Just upping the visibility to public
     // Code for a normal label
     // Maybe has code not in an AngleLabel (align for example).
 }

他们都来自这个班级:

 class CustomLabel
 {
     protected bool Bold;
 }

粗体字段在后续类中公开为公开。

类上没有可用的接口。

现在,我有一个方法可以传递给CustomLabel并设置Bold属性。可以这样做而不必1)找出对象的真实类是什么,2)强制转换为该对象,然后3)为每个标签类型的每个变量制作单独的代码以设置粗体。有点像这样:

 public void SetBold(customLabel: CustomLabel)
 {
     AngleLabel angleLabel;
     NormalLabel normalLabel;


     if (angleLabel is AngleLabel )
     {
        angleLabel= customLabel as AngleLabel 
        angleLabel.Bold = true;
     }

     if (label is Label)
     {
        normalLabel = customLabel as Label
        normalLabel .Bold = true;
     }
 }

如果进行一次演员然后在一个变量上设置粗体,那将是很好的。

我正在思考的是制作第四个类,它只暴露粗体变量并将我的自定义标签投射到该类。

那会有用吗?

如果是这样,它会使用哪种语言? (此示例来自旧版本的Delphi(Delphi 5))。我不知道它是否适用于那种语言,(我仍然需要尝试一下)但我很好奇它是否适用于C ++,C#或Java。

如果没有,任何关于什么会有用的想法? (请记住,没有提供任何接口,我也无法修改类。)

任何人都有猜测?

6 个答案:

答案 0 :(得分:7)

它适用于Delphi。与其使用的类相同的单元中的代码具有对受保护(但不是严格受保护)成员的隐式访问权限,甚至是在另一个单元中声明的成员。您将在CustomLabel

中声明受保护的财产
type
  CustomLabel = class
  private
    FBold: Boolean;
  protected
    property Bold: Boolean read FBold write FBold;
  end;

另一个单元中的粗体设置过程将有自己的CustomLabel后代:

type
  TAccessCustomLabel = class(CustomLabel);

procedure SetBold(customLabel: CustomLabel)
begin
  TAccessCustomLabel(customLabel).Bold := True;
end;

您不能使用as强制转换,因为实际参数永远不会是TAccessLabel的实例。它将是AngleLabelNormalLabel的一个实例,但由于所有三个类从CustomLabel继承的部分都很常见,因此Bold属性在所有这些属性中都是相同的。即使在财产被公布或以后代出版后,这仍然是正确的:

type
  AngleLabel = class(CustomLabel)
  public
    property Bold;
  end;

您可以更改属性的可见性,但不能更改字段。如果您对某个字段尝试相同的操作,则会声明一个 new 字段,该字段具有隐藏继承字段的相同名称。


你可以在C ++中做类似的事情,但它并不像在Delphi中那样普遍,所以它可能会引起一些愤怒,特别是如果你打算编写可移植的代码。

声明第四个类,就像在Delphi中一样。 C ++并不像Delphi那样松散于成员访问,但它具有友谊的概念,在这种情况下也可以正常工作。

class AccessCustomLabel: public CustomLabel
{
  friend void SetLabel(CustomLabel* customLabel);
};

该功能现在可以完全访问班级成员:

void SetLabel(CustomLabel* customLabel)
{
  // Not allowed:
  // customLabel->bold = true

  // Not ordinarily allowed; requires friendship
  reinterpret_cast<AccessCustomLabel*>(customLabel)->bold = true;
}

这是技术上未定义的行为因为我们已经将对象类型转换为它实际上没有的类型。我们依赖于CustomLabel的所有后代具有相同的布局,特别是bold的{​​{1}}成员与AccessCustomLabel位于相同的相对位置任何其他bold后代的成员。


Delphi和C ++代码中的类型转换执行 type punning 。你不会在C#或Java中侥幸逃脱;他们检查了他们的演员表的结果,所以如果CustomLabel没有真正拥有customLabel的实例,你将获得例外。您必须使用反射来访问这些语言中不相关类的受保护成员。证明这超出了我的深度。

答案 1 :(得分:3)

由于父母受到保护,答案是如果没有额外的工作,它不应该以任何语言工作,除非有问题的代码是在自定义版本的后代中执行的。

Delphi可以选择使用protected hack来使其正常工作。 C#和Java可以使用反射。可以使用C ++朋友来使其工作。

但是如果您在CustomLabel中声明Bold为Public,那么该功能将适用于您指定的所有语言。 Delphi,C ++,C#和Java,不需要做任何特殊的事情。

答案 2 :(得分:1)

C ++会使用模板解决这个问题(假设SetBold相当于样本中的Bold):

template<typename T> void SetBold(T t) {
    t.SetBold();
}

答案 3 :(得分:1)

如果您使用的是Delphi 2005或更高版本,则可以使用类帮助程序。类助手可以访问“help”类的受保护字段和方法。使用C#扩展方法可能有类似的东西,但这不是我熟悉的领域。

注意:由于我没有编译器,我无法测试它。

type
   TLabelHelper = class helper for CustomLabel
   public 
     procedure SetBolded(ABold : Boolean);
   end;

procedure TLabelHelper.SetBolded(ABold : Boolean);
begin
  Bold := ABold;
end;

...

Label.SetBolded(True);

答案 4 :(得分:1)

在C#3.0及更高版本中,您可以使用extension methods;这些与Delphi Class Helpers提到的Gerry类似。

它沿着这些方向行进(观看this关键字)。

public static class CustomLabelExtensions // name here is not important, just make it readable
{
    public static void SetBolded(this CustomLabel customLabel, bool newValue)
    {
        customLabel.Bold = newValue;
    }
}   

注意我遗漏了命名空间,就像你在伪代码中所做的那样 但是,请确保您的代码可以看到CustomLabelExtensions,方法是使用其命名空间或显式指定该命名空间。

另请注意,扩展方法只允许您添加方法,而不是属性(来自Delphi背景,对我来说很奇怪)。

然后使用上面的代码:

AngleLabel angleLabel;
NormalLabel normalLabel;
// some code that assigns values to the variables
angleLabel.SetBolded(true);
normalLabel.SetBolded(true);

- 的Jeroen

答案 5 :(得分:0)

您可以做的是在CustomLabel和其他两个类之间添加一个mid类(以CCC命名)。

public class CCC : CustomLabel

这个类应该有一个名为SetBold(bool bold)的函数,它将设置受保护的字段。

public void SetBold(bool bold)
{
    base.Bold = bold;
}

AngleLabel和Label都将继承CCC

并且SetBold(...)的参数将是CCC类型