C# Method Overloading: One Name, Clear Input Choices
Use one method name for closely related operations with different inputs. Learn how the compiler chooses a call, why return types cannot distinguish overloads, and how to keep each version clear and consistent.
What you will understand
- Recognize overloads by their different ordered parameter lists.
- Distinguish valid type or count changes from duplicate signatures.
- Explain why return type and parameter names alone are insufficient.
- Trace exact matches and simple implicit numeric conversions.
- Recognize an ambiguous call without relying on declaration order.
- Understand how optional parameters can overlap overloads.
- Delegate a convenience overload to a complete implementation.
- Build and test a duration formatter that preserves a custom label.
One useful operation can accept different input shapes
Method overloading means declaring more than one method with the same name and different parameter lists in the same type. A reader sees one concept, such as formatting a score, with more than one supported way to supply its inputs. Each declaration still has its own body and contract.
Imagine a report that sometimes needs the standard label Score and sometimes needs Quiz or Practice. You could invent separate names for these closely related operations, but overloads let the input list communicate the distinction. One call supplies only a number; another supplies a label and a number.
The compiler chooses an applicable overload for each ordinary call in these examples. Calling FormatScore does not execute every method with that name. Each editor is a complete separate Program.cs with static helpers inside Program, just like the previous two lessons. Predict which body runs before checking the output.
Program.cs · One operation, two useful input lists
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Score: 8
Quiz: 8
The one-argument call uses FormatScore(int). The two-argument call uses FormatScore(string, int). Both format a score, but the second accepts a custom label. A call executes one selected overload; it does not run both just because their names match.
Compare the ordered parameter types
For the ordinary non-generic methods here, compare the method name and its ordered list of parameter types. A different number of parameters, a different parameter type, or a different order of distinct types can create an overload. More advanced signature rules exist, but you do not need generics or reference modifiers to understand these examples.
FormatScore(int) and FormatScore(string, int) differ in count. Describe(int) and Describe(string) differ in type. Describe(string, int) and Describe(int, string) differ in ordered types. Their return types can be the same; a different result type is not required.
Parameter names, local variable names and method-body statements do not create a new overload signature. Two methods that both take one int collide even if one calls it score and the other calls it points. The intentional duplicate-definition example below makes this boundary visible.
Program.cs · The argument type can distinguish overloads
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Whole number: 8
Text: 8
The two calls display similar digits but supply different types: int and string. Those types select different parameter lists. The compiler does not parse the string to decide that it looks numeric; it matches the typed expression.
Program.cs · Order matters when the types differ
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Label first: Quiz 8
Value first: 8 Quiz
The ordered type lists (string, int) and (int, string) are different. Both declarations are legal. This demonstrates the rule rather than a recommendation to support every possible ordering: an API is often easier to learn with one consistent order.
Program.cs · Renaming a parameter does not create an overload
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Compilation fails: FormatScore already has a member with the same parameter types.Both declarations have the same method name and the same single int parameter type. Parameter names describe meaning and support named calls, but changing score to points cannot make these declarations coexist.
A receiving variable does not choose by return type
A common mistake is to declare int DoubleValue(int) and double DoubleValue(int), expecting an assignment target to decide between them. These declarations already conflict before a useful call can be selected. Changing only the return type does not create a legal pair of overloads.
Think of selection as choosing an operation from the supplied inputs, then using the selected operation’s result. In the ordinary calls taught here, the type of a variable receiving that result is not a menu of desired return types. It cannot make duplicate input signatures valid.
When two operations need the same inputs but mean different things, a different method name may communicate the intent better. Do not add a meaningless extra parameter just to evade a duplicate-signature error. An overload should represent a genuine alternate input contract for the same useful task.
Program.cs · Return type alone cannot distinguish two declarations
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Compilation fails: DoubleValue already has a member with the same parameter types.The receiving int variable does not rescue these declarations. Both have the same name and int input. The return type is not used to distinguish overload signatures. Keep one definition, choose a different name, or change the actual input contract when that represents a useful operation.
Read expression types before reading numeric values
To trace a call, first write the argument count and the type of each expression. Then identify methods whose parameters can receive those arguments. A method may be applicable through an implicit conversion, so an exact match is not required in every program.
In the simple int-versus-double pair, an int argument chooses the int overload and a double argument chooses the double overload. A variable declared double remains a double expression even when it currently holds the whole-number value five. The compiler does not choose int just because that value has no fractional part.
Literal suffixes matter: 5 is int, 5.0 is double and 5.0m is decimal. Use the type appropriate for the calculation. An explicit cast can alter the argument type and therefore the selected overload, but may also lose information. Changing the chosen method is not proof that a conversion was suitable.
Program.cs · Literal types make the choice visible
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
int overload
double overload
5 is an int literal and 5.0 is a double literal. Each has an exact parameter-type match here. Although int can convert to double, the int overload is the better match for the first call. This example has only ordinary numeric overloads; it is not the complete set of C# resolution rules.
Program.cs · The expression type matters, not just its displayed value
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
double overload
int overload
Assigning 5 to a double variable converts it to double. Passing amount therefore selects the double overload. The explicit cast produces an int expression for the second call. Do not cast merely to silence an unwanted match: numeric casts can lose information for other values.
Program.cs · A compatible conversion can make a method applicable
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
double received: 5.0
There is only a double parameter in this program. The int argument can convert implicitly to double, so this method is applicable. An exact type match is not required for every valid call. When several overloads are applicable, the compiler must still find a single best one.
Program.cs · Not every numeric conversion is implicit
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Compilation fails: the double argument cannot implicitly convert to decimal.2.5 is double, while 2.5m would be decimal. No automatic double-to-decimal conversion makes this call valid. Use a decimal literal when decimal is the intended domain, rather than assuming all numeric types freely interchange.
Applicable is different from uniquely best
Several overloads may accept a call. That alone is not an error: the compiler has rules for deciding which applicable method is better. However, there must be a single best choice. The full C# rules are broader than choosing the first declaration or counting how many conversions appear.
Consider Choose(int, double) and Choose(double, int) called with two int arguments. Each candidate is better for a different argument. Neither wins across both positions, so the call is ambiguous. Reordering the declarations does not change that conclusion.
Repair ambiguity by expressing the intended input types clearly or redesigning the API. Choose(1, 1.0) matches the first list directly; Choose(1.0, 1) matches the second. Do not add random casts without understanding which operation is supposed to run. An intentional failure is included so you can inspect the diagnostic.
Program.cs · Two applicable overloads can have no single winner
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Compilation fails: the call is ambiguous between the two Choose methods.For (int, int), the first candidate has the exact first argument and converts the second; the other converts the first and has the exact second. Neither is better across these arguments. Source order does not break the tie. Supplying Choose(1, 1.0) clearly selects the first.
Optional arguments and overloads are separate choices
An optional parameter supplies a default when its argument is omitted. An overload supplies another declaration with a different input list. Both can make short calls possible, but combining them carelessly can produce overlapping contracts that are harder to reason about.
In the displayed FormatScore example, both methods can receive a single int. With these otherwise equivalent matches, C# prefers the declaration that needs no omitted optional argument. The output is Short: 8, rather than the default label from the longer method. This is a specific tie-breaking example, not a promise that every optional-overload combination is unambiguous.
Prefer one clear optional method when only a simple default differs, or a small overload family when the input shapes deserve distinct contracts. Avoid adding every possible variation. Ask whether a reader can predict the chosen operation without memorizing obscure resolution cases.
Program.cs · An optional parameter can overlap another overload
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Short: 8
Quiz: 8
Both definitions can accept the one-int call, but with these otherwise equivalent matches the version that needs no omitted optional parameter wins. The two-argument call selects the longer list. Do not assume any overlap is automatically ambiguous, and avoid such overlap when a simpler interface would be clearer.
Share the implementation and preserve every promised input
A convenience overload can forward to a complete overload. FormatScore(int) supplies a standard label and calls FormatScore(string, int). The second overload owns the formatting rule, so both call forms stay consistent when the rule changes.
Check the direction of delegation. A helper that receives a custom label must not silently throw it away by forwarding only the number to a less informative overload. Type correctness cannot catch that loss of meaning. The guided repair below fixes this design error without introducing another overload.
Overloading is different from overriding. These static helper examples choose among parameter lists; overriding later lets a derived class change inherited virtual behavior. You do not need inheritance for this lesson. Keep overloads focused on one concept, test every call shape, and trace any forwarding call through to its final implementation.
Program.cs · Let a convenience overload share the formatting rule
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Score: 8
Quiz: 8
The short overload supplies a default label by calling the two-parameter overload. That second signature is selected because the forwarded arguments are string and int. The output rule lives in one place. This is not an infinite self-call: the parameter list changes and the selected longer method returns a string.
Guided repair: preserve the custom label
FormatScore("Quiz", 8) prints Score: 8. The call selects the correct overload, but that overload discards the label. Repair the forwarding direction so custom labels survive and the standard label remains available.
Program.cs · Same types can hide a semantic mistake
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Score: 8
This compiles but loses the caller’s custom label. The two-parameter overload forwards only the score to a helper that knows nothing about Quiz. Overload selection is working; the design forwards information in the wrong direction.
Repair checkpoints
- Keep FormatScore(string label, int score) responsible for the formatted result.
- Use both label and score in that full implementation.
- Let FormatScore(int score) call FormatScore("Score", score).
- Check that Quiz produces Quiz: 8 and the short call produces Score: 8.
- Do not duplicate the formatted string in both bodies or accidentally call the same overload forever.
Reveal the repair and explanation
Program.cs · Forward from fewer inputs to the complete contract
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Quiz: 8
Score: 8
The complete overload uses every input it promises to respect. The convenience overload adds a default label and delegates to it. Changing the shared output rule now updates both forms while preserving an explicitly supplied custom label.
Independent lab: two duration-formatting overloads
Create two FormatDuration overloads for a practice report: one accepts whole minutes and supplies Practice as the label, while the other accepts a custom label and whole minutes. Format the same duration rule through one implementation.
Acceptance checklist
- Provide static string FormatDuration(int minutes).
- Provide static string FormatDuration(string label, int minutes).
- Delegate the one-parameter overload to the two-parameter overload with label Practice.
- Calculate complete hours with minutes / 60 and remaining minutes with minutes % 60.
- Return text from both methods and leave Console.WriteLine in Main.
- For 75 print Practice: 1h 15m; for Reading and 45 print Reading: 0h 45m.
- Assume nonnegative whole minutes and a supplied non-null label; clock times and localization are outside this exercise.
Program.cs · Duration formatting starter
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Practice: 75 minutes
Practice: 45 minutes
Both overloads compile, but neither implements hours and remaining minutes, the second ignores label, and the formatting is duplicated. Use the lab contract to preserve custom labels and place the duration calculation in one implementation.
Show hints
- Start by implementing the full two-input contract.
- The short overload should contain a return call to the full overload.
- Use integer division and remainder, not a rounded decimal hour count.
Reveal the reference solution
Program.cs · Duration formatting reference solution
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Practice: 1h 15m
Reading: 0h 45m
The int-only overload delegates with Practice as its label. The string-and-int overload divides by sixty for complete hours and uses remainder for leftover minutes. Both return text; Main prints it. This exercise accepts nonnegative whole minutes and does not represent a clock time or a time zone.
Changed-input checks
- 0 minutes → Practice: 0h 0m.
- 59 minutes → Practice: 0h 59m.
- 60 minutes → Practice: 1h 0m.
- 125 minutes → Practice: 2h 5m.
- A custom label Review and 120 minutes → Review: 2h 0m.
- Changing the full formatting rule should affect both overloads.
Recap: choose a clear input contract
Read the ordered parameter types, identify applicable candidates, and check that one method is the best choice. Keep each overload faithful to its inputs.
same name, different count- Support related operations with different numbers of inputs.
same name, different types- Select an appropriate contract from typed arguments.
ordered type list- Changing the order of distinct types can distinguish declarations.
parameter names- Describe inputs; changing names alone cannot distinguish overloads.
return type- Describes the result; it alone cannot create another overload.
applicable versus best- An accepted conversion makes a candidate possible, but one best method is still required.
optional argument- Supplies an omitted default; it is not another declaration.
delegating overload- Supply convenience inputs and share the full implementation.
Frequently asked questions
Can two overloads differ only in return type?
No. The same name and parameter types collide even if the return types differ. The receiving variable does not make that declaration pair legal.
Does renaming an input create another overload?
No. Parameter names can be used by named arguments, but renaming an int parameter does not make another one-int declaration distinct.
Does a call run all methods with that name?
No. An ordinary call selects one best applicable overload. Its body may then explicitly call another method, as in the delegation example.
Does C# pick whichever declaration appears first?
No. Applicability and overload-resolution rules determine the choice. Moving the ambiguous methods around does not remove ambiguity.
Are optional parameters the same as overloads?
No. A default belongs to one declaration, whereas overloads are separate declarations. They can overlap, so keep the public interface predictable.
Is overloading the same as overriding?
No. Overloading concerns different parameter lists for a name. Overriding concerns changing inherited virtual behavior, which later object-oriented lessons cover.
Which overload does this call select?
Identify signatures, trace numeric argument types and recognize ambiguous calls. Read the explanation after checking your answers.
Next step and references
Overloads give a related operation different input lists. Trace argument types and conversions, require one best candidate, and share the implementation without discarding supplied information.
Next is C# Object-Oriented Thinking. You will connect state and behavior, distinguish a class from an object, and choose meaningful responsibilities before writing a model.
Official references
Original SovranCode explanations, examples and exercises. Technical details were checked against these references.
- Microsoft Learn: Methods and overload signatures
- C# specification: Methods
- Microsoft Learn: Named and optional arguments
Last updated: .