Skip to main content

Inheritance Dispatch

SAL can inherit implementation from multiple base classes. C# has one direct class base. Ice Porter combines real inheritance, contained secondary objects, forwarding members, and generated late-bind interfaces to preserve the original call routes.

Generated inheritance and dispatch routes
  1. CDerivedFinal application objectActive
  2. CBase1Real C# base class
  3. Forwarding memberGenerated property or method
  4. CBase2 instanceOwned secondary object
  5. LateBind interface_derived → final object
Step 1 of 2Call an inherited member

CDerived inherits directly from CBase1 in C#. Its base state belongs to the same object.

CDerived : CBase1

A conceptual sequence, not a live runtime trace. Playback advances every four seconds. The full explanation follows below.

Read the Object Relationships

CDerived ── inherits ──► CBase1

├── owns ──────────► _CBase2 instance
│ │
◄──── _derived ────────┘

└── implements ───► CBase2.LateBind (when generated)

The first-base portion belongs to the same C# object as CDerived. The secondary-base state lives in the contained _CBase2 instance. Its _derived reference points back to the final application object. These relationships have different purposes; replacing them all with direct inheritance or interfaces would lose part of the behavior.

First-Base Calls

A member inherited through CBase1 follows ordinary C# inheritance. For virtual members, dispatch can reach an override on CDerived. There is no secondary object hop for that first-base state.

Trace: caller → inherited member → selected implementation → caller.

Secondary-Base Calls

A secondary member exposed as CDerived.Save() can be a forwarding method that invokes _CBase2.Save(). Generated properties similarly expose fields owned by the secondary instance.

Trace: caller → CDerived forwarding member → owned CBase2 implementation → forwarding return → caller.

Preserve the identity and lifetime of the owned instance. Creating a fresh secondary object on every call would reset its fields. Copying the implementation into several wrappers risks creating independent copies of state or inconsistent fixes.

Calls Back to a Derived Implementation

A method inside CBase2 cannot use ordinary C# overriding to reach CDerived, because CDerived does not inherit from that secondary class. For generated late-bound calls, the secondary object uses _derived and a generated interface to route to the final implementation.

Trace: secondary method → generated late-bind wrapper → _derived cast to the late-bind interface → final derived implementation. The generated wrapper includes the fallback behavior for a final object that does not supply that interface implementation.

Calling the secondary implementation directly during a refactor can bypass this route. Conversely, changing a qualified base call to a virtual/late-bound call can introduce an override that the original code deliberately avoided. Preserve the distinction between a normal call, a qualified base call, and a late-bound call.

A Focused Regression Test

Create a small hierarchy with a first-base field, a secondary-base field, and a derived implementation of a method invoked through a late-bound expression. Give each implementation a distinct trace label.

  1. Read and write the first-base field through the derived object; confirm one persistent value.
  2. Read and write the secondary field through its generated property; confirm the owned object sees the same value.
  3. Call the secondary method through the derived wrapper and check its return value and state changes.
  4. Trigger the late-bound expression from within the secondary branch; confirm it selects the derived implementation where the source does.
  5. Exercise any qualified base calls and casts used by the source; verify that they retain their original meaning.

For visual multiple inheritance, add creation/destruction and control-state checks. The simplified object diagram does not describe every designer-generated visual relationship.

See Multiple Inheritance for generated code examples and Late Bind Calls for the translation details.