Function and Method Overloading¶
Sharpy supports defining multiple functions or methods with the same name, provided their parameter signatures differ. This follows C# overloading semantics.
For general overload resolution rules (applicable to both functions and methods), see Function Parameters — Overload Resolution Rules.
Module-Level Function Overloading¶
Multiple def statements with the same name can appear at module level if they have different parameter signatures (arity or types). Unlike Python, which replaces previous definitions, Sharpy creates overloads — consistent with class method behavior.
def describe(x: int) -> str:
return "int:" + str(x)
def describe(x: str) -> str:
return "str:" + x
def describe(x: int, verbose: bool) -> str:
return f"int({x}, verbose={verbose})"
print(describe(42)) # "int:42"
print(describe("hello")) # "str:hello"
print(describe(42, True)) # "int(42, verbose=True)"
Module-level overloads are importable across files:
# main.spy
from lib import describe
print(describe(42)) # Resolves to describe(int) in lib
print(describe("hello")) # Resolves to describe(str) in lib
Implementation - ✅ Native - C# supports method overloading directly. Each overload emits as a separate static method in the module class.
Basic Method Overloading¶
Methods can be overloaded by parameter count (arity) or parameter types:
class Formatter:
def format(self, value: int) -> str:
return "int:" + str(value)
def format(self, value: str) -> str:
return "str:" + value
def format(self, value: int, width: int) -> str:
return str(value).rjust(width)
f = Formatter()
print(f.format(42)) # "int:42"
print(f.format("hello")) # "str:hello"
print(f.format(42, 10)) # " 42"
Implementation - ✅ Native - C# supports method overloading directly.
Constructor Overloading¶
Multiple __init__ methods can be defined with different signatures. Constructor chaining is supported via self.__init__():
class Point:
x: int
y: int
z: int
def __init__(self, x: int, y: int, z: int):
self.x = x
self.y = y
self.z = z
def __init__(self, x: int, y: int):
self.__init__(x, y, 0)
def __init__(self, x: int):
self.__init__(x, 0, 0)
Constructor chaining rules:
- self.__init__(...) in a constructor body chains to another constructor of the same class
- super().__init__(...) chains to the base class constructor
- Chaining calls are lowered to C# constructor initializers (: this(...) and : base(...))
Implementation
- 🔄 Lowered - self.__init__(...) → C# : this(...) constructor initializer
- 🔄 Lowered - super().__init__(...) → C# : base(...) constructor initializer
Overloading with Default Parameters¶
Be cautious when combining overloads with default parameters, as this can create ambiguity:
class Logger:
def log(self, msg: str) -> None:
print(msg)
def log(self, msg: str, level: int) -> None:
print(f"[{level}] {msg}")
logger = Logger()
logger.log("hello") # Calls log(str) — exact match
logger.log("hello", 2) # Calls log(str, int)
Avoid overloads that differ only in having additional defaulted parameters:
# ❌ Ambiguous — both match log("hello")
class BadLogger:
def log(self, msg: str) -> None: ...
def log(self, msg: str, level: int = 0) -> None: ...
Operator Method Overloading¶
Dunder methods for operators can be overloaded to accept different operand types:
class Vector:
x: float
y: float
def __init__(self, x: float, y: float):
self.x = x
self.y = y
def __add__(self, other: Vector) -> Vector:
return Vector(self.x + other.x, self.y + other.y)
def __eq__(self, other: Vector) -> bool:
return self.x == other.x and self.y == other.y
Implementation - ✅ Native - Each dunder overload maps to a separate C# operator overload.
Restrictions¶
- Cannot differ only by return type. Overloads must differ in parameter count or types. Two methods with identical parameter signatures but different return types are rejected.
# ❌ ERROR: Duplicate method signature
class Bad:
def process(self) -> int: ...
def process(self) -> str: ...
-
selfis excluded from signature comparison. The implicitselfparameter is not considered when comparing overload signatures. -
Overloads must be in the same class. A method in a subclass with the same name and signature as a base class method is an override, not an overload (use
@override).
Diagnostics¶
| Code | Level | Description |
|---|---|---|
| SPY0353 | Error | Ambiguous overload — multiple overloads match equally well |
| SPY0354 | Error | No matching overload — no overload matches the argument types |
| SPY0355 | Error | Duplicate method signature — two overloads have identical parameter signatures |
See Also¶
- Function Parameters — General overload resolution rules and named argument interaction
- Constructors — Constructor definition and chaining
- Operator Overloading — Dunder methods for operator overloading