61. params keyword
The params keyword lets a method accept a variable number
of arguments, which are treated as an array.
Example:
void PrintNumbers(params int[] numbers) {
foreach (var num
in numbers) Console.WriteLine(num);
}
PrintNumbers(1, 2, 3, 4);
// Treated as an int[]
Key Features:
- Allows
passing multiple arguments without manually creating an array. - Must
be the last parameter in a method signature.
Use params for methods like Console.WriteLine that accept
multiple inputs.
62. optional parameters
Optional parameters allow omitting arguments by
providing default values.
In C#, optional parameters allow a method to define default
values for some or all of its parameters. This means that when calling the
method, you can omit these parameters, and the method will use the
default values instead.
This feature reduces the need for overloaded methods
when you want to provide flexibility in function calls.
How Do Optional Parameters Work?
To declare an optional parameter, assign it a default
value in the method signature:
void Greet(string name = “Guest”) {
Console.WriteLine($”Hello, {name}!”);
}
Now, you can call Greet() in two ways:
Greet(); //
Outputs: Hello, Guest!
Greet(“Jimmy”);
// Outputs: Hello, Jimmy!
If no argument is provided, “Guest” is used
by default.
63. named arguments
Named arguments allow specifying parameters by name
instead of position.
Example:
void OrderPizza(string size, string topping) { }
OrderPizza(topping: “Pepperoni”, size:
“Large”);
Useful when calling methods with many parameters.
Multiple Optional Parameters
You can define multiple optional parameters:
void PrintInfo(string name = “Unknown”, int age =
30) {
Console.WriteLine($”Name: {name}, Age: {age}”);
}
Usage:
PrintInfo();
// Outputs: Name: Unknown, Age: 30
PrintInfo(“Szabi”); // Outputs: Name: Szabi, Age: 30
PrintInfo(“Szabi”, 25); // Outputs: Name: Szabi, Age: 25
ο· If both
parameters are omitted, the defaults “Unknown” and 30 are used.
ο· If only name is
provided, age still defaults to 30.
Using Optional Parameters with Named Arguments
Optional parameters work well with named arguments,
allowing you to skip specific parameters while providing values for others.
Example:
void OrderPizza(string size = “Medium”, string
topping = “Cheese”, bool extraCheese = false) {
Console.WriteLine($”Size: {size}, Topping: {topping}, Extra Cheese:
{extraCheese}”);
}
Different ways to call OrderPizza():
OrderPizza(); //
Size: Medium, Topping: Cheese, Extra Cheese: False
OrderPizza(size: “Large”);
// Size: Large, Topping: Cheese, Extra Cheese: False
OrderPizza(topping: “Pepperoni”, extraCheese:
true);
// Size: Medium, Topping: Pepperoni, Extra Cheese: True
Using named arguments improves readability by
specifying parameter names explicitly.
How Does This Compare to Method Overloading?
Before optional parameters were introduced, method
overloading was commonly used to achieve similar functionality.
Without optional parameters:
void Greet() {
Greet(“Guest”); // Calls the main method
}
void Greet(string name) {
Console.WriteLine($”Hello, {name}!”);
}
This works but requires writing multiple method versions.
With optional parameters:
void Greet(string name = “Guest”) {
Console.WriteLine($”Hello, {name}!”);
}
This is cleaner, requires less code, and is easier
to maintain.
Limitations & Gotchas
Despite their benefits, optional parameters have some
important limitations:
1. Optional Parameters Must Come Last
All optional parameters must be at the end of the
parameter list. You cannot place a required parameter after an optional
one.
π« Invalid example:
void WrongMethod(int x = 10, string name) { } // β Compilation error!
β Corrected version:
void CorrectMethod(string name, int x = 10) { } // β Works!
The compiler must be able to distinguish between required
and optional arguments, so required parameters must always come first.
2. Default Values Are Fixed at Compile Time
Optional parameters must be compile-time constants.
They cannot be set dynamically using variables or method calls.
π« Invalid:
int GetDefaultAge() {
return 30;
}
void SetAge(int age = GetDefaultAge()) { } // β Error: Not a constant
β
Valid alternative: Use
method overloading instead:
void SetAge() {
SetAge(30); // Calls overloaded method
}
void SetAge(int age) {
Console.WriteLine($”Age: {age}”);
}
This ensures that the default value is determined at
runtime.
3. Changing Default Values Can Break Code
Since optional parameters are resolved at compile-time,
changing a default value affects all compiled code that depends on it.
Example:
void DisplayMessage(string message = “Default
message”) {
Console.WriteLine(message);
}
If a library is compiled with “Default message”
and later the method changes to:
void DisplayMessage(string message = “Updated
message”) { }
Existing compiled client code will still use “Default
message”, since the default value is baked into the compiled code.
Solution? Avoid changing default values in widely
used APIs or use method overloading instead.
Should You Use Optional Parameters?
β Advantages:
- Less
Code, More Readability - No
need for multiple method overloads for simple variations. - Improves
Function Flexibility - Allows
flexible calls without unnecessary overloads. - Works
Well with Named Arguments - Lets
you skip parameters without specifying all previous ones. - Reduces
API Complexity - Library
and API design benefit from fewer redundant methods.
β Disadvantages:
- Limited
to Compile-Time Constants - Cannot
use dynamic values (e.g., current time, database values). - May
Cause Versioning Issues - Default
values are baked into compiled code, which can lead to unintended
behavior when updating a library. - Not
Always Clear to New Developers - Code
might be harder to read if there are too many optional parameters.
When to Use Optional Parameters?
β Best Uses:
- Simplifying
overloaded methods (e.g., default values for optional behavior). - Library
and API design where function calls should be flexible. - Reducing
boilerplate code in methods with sensible defaults.
π« Avoid When:
- The default
value is not a constant (use method overloading instead). - It could
cause breaking changes in library updates. - The
method has too many optional parameters, making calls hard to
understand.
πΉ Optional parameters reduce
redundant code and increase flexibility, but they should be used cautiously
to avoid hidden pitfalls. If your method has more than 3-4 optional
parameters, it may be a sign that method overloading or a parameter
object is a better approach.
64. expression-bodied member
What Are Expression-Bodied Members?
Expression-bodied members in C# provide a concise way
to define methods, properties, constructors, and even indexers using the => lambda
expression syntax instead of the traditional block { }. This is primarily
used to simplify short, single-expression members by reducing
boilerplate code.
Basic Example
Instead of writing a method in the usual way:
class Example {
public int
Square(int x) {
return x * x;
}
}
We can simplify it using an expression-bodied method:
class Example {
public int
Square(int x) => x * x;
}
Both versions produce the exact same result, but the
second one is shorter and more readable when the method consists of a
single expression.
Where Can You Use Expression-Bodied Members?
Expression-bodied syntax can be applied to:
1. Methods
public int Multiply(int a, int b) => a * b;
2. Properties (Getters)
public int MyProperty => 42;
Equivalent to:
public int MyProperty { get { return 42; } }
3. Constructors
class Example {
private string
_name;
public
Example(string name) => _name = name;
}
4. Destructors
class Example {
~Example() =>
Console.WriteLine(“Destructor called”);
}
5. Indexers
class MyClass {
private int[] arr
= { 1, 2, 3 };
public int
this[int index] => arr[index];
}
65. local function
A local function is a method declared inside
another method and is only accessible within that enclosing method.
Introduced in C# 7.0, local functions provide a way to encapsulate
helper logic within a method without exposing it externally.
Basic Example
void OuterMethod() {
Console.WriteLine(“Outer method called”);
void
LocalFunction() {
Console.WriteLine(“Local function executed”);
}
LocalFunction(); // Can be called
within the same method
}
ο· LocalFunction() exists
only inside OuterMethod().
ο· It cannot be
accessed from outside, ensuring encapsulation of logic.
Key Benefits of Local Functions
β
Encapsulation of Helper
Logic
- Keeps
small, one-time-use functions hidden within a method, avoiding
unnecessary clutter in the class.
β
More Readable &
Maintainable
- Unlike
separate helper methods, local functions are declared close to where
they are used, improving readability.
β
Better Performance than
Lambdas
- Local
functions do not allocate extra memory like lambda expressions,
making them more efficient.
How Are Local
Functions Different from Lambda Expressions?
Β·
A lambda expression (e.g., x => x * x) is an anonymous function, while a local function is a named
function.
Β·
β
Local
Function (More Readable & Optimized)
void CalculateAndPrint(int x) {
int Square(int n)
=> n * n; // Local function
Console.WriteLine(Square(x));
}
- Better
performance (no heap allocation). - Easier
to debug due to having a function name.
β Lambda Alternative (Less
Readable & Allocates Memory)
void CalculateAndPrint(int x) {
Func<int,
int> Square = n => n * n; //
Lambda
Console.WriteLine(Square(x));
}
ο· Less efficient,
as Func<int, int> creates an extra delegate object.
ο· Harder to debug
(no meaningful function name in error messages).
Limitations of Local Functions
β Not Always Readable
- If
overused, local functions can make methods too long and reduce
clarity.
β Limited Scope
- Unlike
normal methods, local functions cannot be called from outside the
enclosing method.
Β·
When Should You Use Local
Functions?
Β·
β When a function is only needed within a single method (e.g., data
transformations or input validation).
β When performance matters (avoid unnecessary delegate allocation).
β When you want more readable, structured code than using lambdas.
Β·
π« Avoid if the function is large or needs to be reused elsewhereβuse
a normal method instead.