Ticker

6/recent/ticker-posts

Header Ads Widget

The Essential C#/.NET Handbook # 61 - 65

The Essential C#/.NET Handbook # 61 - 65 - Encyclopedia of C-Sharp Notions

 

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:

  1. Less Code, More Readability
    • No need for multiple method overloads for simple variations.
  2. Improves Function Flexibility
    • Allows flexible calls without unnecessary overloads.
  3. Works Well with Named Arguments
    • Lets you skip parameters without specifying all previous ones.
  4. Reduces API Complexity
    • Library and API design benefit from fewer redundant methods.

Disadvantages:

  1. Limited to Compile-Time Constants
    • Cannot use dynamic values (e.g., current time, database values).
  2. May Cause Versioning Issues
    • Default values are baked into compiled code, which can lead to unintended behavior when updating a library.
  3. 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.