10. Inheritance and interfaces
10.1 Base classes and overriding
CS-INHERIT-001. A class has at most one compatible base class through inherits. It may implement multiple interfaces. Abstract classes and actions follow ordinary CLR abstract contracts; an abstract class cannot be instantiated, and a concrete descendant must satisfy all abstract obligations.
CS-INHERIT-002. An action intended for polymorphic replacement is overridable. A replacing action declares overrides base.Member after its header. A matching derived signature without that relationship is a compilation error rather than an implicit override or accidental hide. Normal CLR signature, accessibility and async-contract compatibility applies. call base.Member(...) explicitly invokes the base implementation and does not declare the override relationship.
public class FeePolicy
overridable action Calculate(amount as Decimal)
returns Decimal
return amount * 0.015
end action
end class
public class PreferredFeePolicy
inherits FeePolicy
action Calculate(amount as Decimal)
overrides base.Calculate
returns Decimal
return 0
end action
end class
CS-INHERIT-003. Constructors are not inherited. A derived constructor uses explicit delegation when no accessible parameterless base constructor is available. Source access checks apply to base calls, inherited members and external CLR contracts. DirectSharp does not replace imported class behavior or invent another dispatch model.
Property-based indexers inherit under the property contract and use virtual/override/abstract as defined in chapter 9. A base-typed reference dispatches to the runtime instance's override. The action spellings above remain unchanged. Interfaces may declare indexer contracts under CS-CLASS-015; this does not introduce implicit member mapping.
10.2 Interface intent and member mapping
CS-INTERFACE-001. An authored class declares interface intent through member-level mappings or a named implementation body block. A class-header interface list alone does not make arbitrary same-name class members implementations. Outside a named implementation body block, an implementing member records its mapping explicitly. Inside such a block, the block supplies intent as defined below. Imported compiled CLR types retain their actual interface maps.
public interface ICustomerReader
action Find(id as Guid)
returns Customer
end interface
public class CustomerReader
implements ICustomerReader
action Find(id as Guid)
implements ICustomerReader
returns Customer
return nothing
end action
end class
CS-INTERFACE-002. Member-level implements Interface is allowed only when normal binding finds exactly one compatible member in that interface. With no compatible target or multiple compatible targets, reject it and require explicit disambiguation. implements Interface.Member identifies the target directly. Selection must validate the real signature, including parameter types, ref-kind, result and async ABI.
CS-INTERFACE-003. One member may list several compatible interface relationships explicitly. A class-local with alias Name on an implemented interface shortens subsequent mappings in that class; it does not create a runtime interface or change the contract. Normal ambiguity checks still apply.
CS-INTERFACE-004. implements Interface ... end implements containing member declarations is a named implementation body block. It declares the class's relationship to that interface without requiring a repeated class-header declaration. A same-name member with a compatible signature inside the block may satisfy the corresponding interface member without an individual implements marker. This applies to compatible actions and properties; normal CLR signature, generic, ref-kind and async-contract validation remains required.
The block may also contain ordinary supporting members. They do not satisfy unrelated or missing requirements. A renamed implementation declares implements Member relative to the enclosing interface, or uses an explicit qualified mapping. Ambiguous mappings and incompatible/missing requirements are errors. Block members retain their normal class API visibility; they are not automatically hidden interface-only implementations. Grouped class-header interface lists remain declaration lists rather than implementation body blocks.
public class CustomerReader
implements ICustomerReader
public action Find(id as Guid)
returns Customer
return nothing
end action
end implements
end class
This replaces the preceding CustomerReader declaration and uses its ICustomerReader contract. Find implements the contract through the named block. Moving Find outside that block requires a member-level mapping; a class-header relationship alone does not supply that mapping.
class CustomerReader
implements
ICustomerReader with alias Reader
ICustomerLookup with alias Lookup
end implements
action Find(id as Guid)
implements Reader.Find
implements Lookup.Find
returns Customer
return nothing
end action
end class
This declaration replaces the preceding CustomerReader and assumes a compatible ICustomerLookup.Find contract. The two mappings share one body deliberately. Supporting helper members remain ordinary class members; placing a helper near interface mappings does not turn it into a requirement or satisfy a missing requirement.
10.3 CLR forwarding and diagnostics
When the DirectSharp implementation name differs from the interface member, the backend can generate a forwarding member. For an authored Lookup mapped explicitly to ICustomerReader.Find, the shape is:
public class CustomerReader : ICustomerReader
{
public Customer? Lookup(Guid id) => null;
Customer? ICustomerReader.Find(Guid id) => Lookup(id);
}
The authored member retains its normal class API visibility. Generated interface forwarding must preserve ref-kind, exception propagation, generic constraints and actual result contract, including Task versus ValueTask where applicable. It must not accidentally make all class members interface implementations through C# signature matching.
Required diagnostics include missing abstract/interface requirements, incompatible overrides, undeclared same-signature overrides, invalid/inaccessible bases, no or multiple short-form interface targets, incompatible shared implementations, and ambiguous class-local aliases.
Specification contents · Previous: 9. Classes, data and members · Next: 11. Enums, flags and one-of types