SEALED ToString() IN C# 10
The argument concerns the possibility of preventing the override of the ToString () method of the Object class within the hierarchy of Type records.
public virtual string? ToString();
This method of the Object class is inherited by all types and represents textual information on the object on which the method is invoked. If we don’t properly redefine the ToString () method, it returns the name of the Type on which it was invoked. In previous lessons when we talked about records we saw that the ToString () method was redefined by the compiler to have the following information about the record.
Libro {Titolo=” L’isola misteriosa”, Autore=” Jules Verne”}
using System; Libro libro1 = new Libro("L'isola misteriosa","Jules Verne"); Console.WriteLine(libro1);//LIBRO record Libro { #nullable enable public string Titolo {get;init;} public string Autore {get;init;} #nullable disable public Libro() { } public Libro(string titolo, string autore) { Titolo=titolo; Autore=autore; } }
THE CLASS StringBuilder
This class which is part of the BCL (Base Class Library) represents a string which however is mutable unlike the string class which is immutable. This class offers us a wide range of methods to modify the string. The ToString () method used by the compiler to extract record information uses just this class. Let’s now override the ToString () method using the StringBuilder class in the Book record. Below is the code. Using the dotnet run command what the ToString () method returns to us is shown in the following screenshot.
using System; using System.Text; Libro libro1 = new Libro("L'isola misteriosa","Jules Verne"); Console.WriteLine(libro1);//LIBRO record Libro { #nullable enable public string Titolo {get;init;} public string Autore {get;init;} #nullable disable public Libro() { } public Libro(string titolo, string autore) { Titolo=titolo; Autore=autore; } //OVERRIDE DI ToString() public override string ToString() { StringBuilder builder = new StringBuilder(); builder.Append("RECORD: Libro - "); builder.Append(Titolo); return builder.ToString(); } }
The ToString() method, if applied to an instance of the StringBuilder class, returns the text constructed with the instance itself. Now let’s take the subtype code of the Book record and in the DigitalBook record we redefine the ToString () method. The output is shown in the figure.
using System; using System.Text; Libro libro1 = new Libro("L'isola misteriosa","Jules Verne"); LibroDigitale libro2 = new LibroDigitale("L'isola misteriosa","Jules Verne", "6 ORE"); Console.WriteLine(libro1);//LIBRO Console.WriteLine(libro2); record Libro { #nullable enable public string Titolo {get;init;} public string Autore {get;init;} #nullable disable public Libro() { } public Libro(string titolo, string autore) { Titolo=titolo; Autore=autore; } //OVERRIDE DI ToString() public sealed override string ToString() { StringBuilder builder = new StringBuilder(); builder.Append("RECORD: Libro - "); builder.Append(Titolo); return builder.ToString(); } } record LibroDigitale:Libro { #nullable enable public string Durata {get;init;} #nullable disable public LibroDigitale() { } public LibroDigitale(string titolo,string autore, string durata) { Titolo=titolo; Autore=autore; Durata=durata; } //OVERRIDE DI ToString public override string ToString() { string s = base.ToString(); StringBuilder builder = new StringBuilder(); builder.Append(s); builder.Append(" -- DURATA: -- "); builder.Append(Durata); return builder.ToString(); } }
THE KEYWORD SEALED
C# 10 provides us with a method to prevent the ToString () method from being overridden in our sub-types records. Insert the sealed keyword into the redefined ToString () method of the Book record.
using System; using System.Text; Libro libro1 = new Libro("L'isola misteriosa","Jules Verne"); LibroDigitale libro2 = new LibroDigitale("L'isola misteriosa","Jules Verne", "6 ORE"); Console.WriteLine(libro1);//LIBRO Console.WriteLine(libro2); record Libro { #nullable enable public string Titolo {get;init;} public string Autore {get;init;} #nullable disable public Libro() { } public Libro(string titolo, string autore) { Titolo=titolo; Autore=autore; } //OVERRIDE DI ToString() /*By inserting the sealed keyword in C# 10 we are in fact preventing the override of the ToString () method in the sub types of the Book record that is, it is no longer possible to override of the ToString() method in the derived type DigitalBook. */ public sealed override string ToString() { StringBuilder builder = new StringBuilder(); builder.Append("RECORD: Libro - "); builder.Append(Titolo); return builder.ToString(); } } record LibroDigitale:Libro { #nullable enable public string Durata {get;init;} #nullable disable public LibroDigitale() { } public LibroDigitale(string titolo,string autore, string durata) { Titolo=titolo; Autore=autore; Durata=durata; } //OVERRIDE DI ToString public override string ToString() { string s = base.ToString(); StringBuilder builder = new StringBuilder(); builder.Append(s); builder.Append("-- DURATA: -- "); builder.Append(Durata); return builder.ToString(); } }
RAW STRINGS
Raw String Literals (string literals) in C# represent a new feature introduced to simplify the writing of complex strings, particularly useful when working with strings that contain special characters, multiple lines, or when you want to avoid the need for escapes (escape characters).
What is a Raw String Literal?
A Raw String Literal is a string that can be written exactly as it appears, without the need to use escape characters for symbols such as quotation marks, reverse slashes (\), or new lines. This makes raw string literals particularly useful for handling multi-line strings, JSON code, XML, SQL queries, or other strings with complex formatting.
Syntax of Raw String Literals
The syntax for Raw String Literals involves the use of triple double quotes (“””) to delimit the string. Here is a basic example:
string rawString = “””
Questo è un esempio di una raw string.
Non c’è bisogno di usare caratteri di escape.
Anche le virgolette doppie ” non devono essere escape.
Puoi scrivere su più linee senza problemi.
“””;
In this example, the rawString contains exactly what is written between the triple double quotes, including newlines and quotation marks.
Advantages of Raw String Literals.
1. No Escaping Needed: You can include special characters, such as quotation marks or reverse slashes, directly in the string without escaping.
2. Multi-line Strings: You can write strings that span multiple lines, maintaining the original format without interrupting or adding special characters.
3. Increased Readability: Strings that contain complex formatting, such as code or markup, are more readable and easier to maintain.
4. Support for Spaces and Indentations: The syntax of raw string literals allows the indentation of strings to be maintained as desired, making it easier to write readable code.
Usage Examples.
Example 1: JSON code
string json = “””
{
“nome”: “Mario”,
“età”: 30,
“indirizzo”: {
“via”: “Via Roma”,
“città”: “Roma”
}
}
“””;
In this example, a JSON code is inserted directly into the string without the need for escape characters or complications.
Example 2: Multi-line SQL Query
string query = “””
SELECT *
FROM utenti
WHERE età > 25
ORDER BY nome ASC;
“””;
Here, an SQL query is written in multiple rows while maintaining a clear and readable structure.
Considerations
– When using raw string literals, additional double quotes (e.g., “”””) can be included at the beginning and end of the string to avoid conflicts if the string contains triple double quotes.
– Raw string literals are particularly useful in scenarios where you need to work with strings that include code, structured data, or text on multiple lines, reducing the need for manipulation and simplifying code maintenance.
Conclusion
Raw String Literals in C# offer a powerful and flexible way to handle complex strings, significantly improving code readability and maintainability. This feature represents a major improvement in string handling, particularly useful for developers working with complex formatting or multi-line strings.
AUTO-DEFAULT STRUCTS
A feature called Auto-Default Structs was introduced in C# 12, which further simplifies the use of structures (structs) in the language. This feature allows you to automatically initialize structs with default values without the need to explicitly declare a default constructor.
What is an Auto-Default Struct?
An Auto-Default Struct is a structure that, when instantiated, is automatically initialized with default values for all its fields, even if no default constructor has been defined. Prior to this feature, structs were always initialized with default values, but to achieve custom behavior, a constructor had to be explicitly defined.
Example of Auto-Default Struct
Let us imagine that we have a struct Punto representing a 2D point:
public struct Point
{
public int X;
public int Y;
}
With Auto-Default Structs, you can instantiate Point without needing a constructor, and X and Y will be automatically initialized to their default values (which for numeric types is 0):
Point p = new Point(); // X = 0, Y = 0
Even if you do not specify new Point(), but simply use Point p;, the variables X and Y are automatically initialized to their default values.
Advantages of Auto-Default Structs.
1. Reduced Boilerplate Code: There is no need to explicitly write a default constructor or manually initialize structure fields.
2. Simplicity: Makes the use of structures simpler and more straightforward, reducing the possibility of errors due to non-initialization of fields.
3. Consistency: Ensures that structures are always initialized in a predictable way, with default values defined for primitive types.
Considerations
– Auto-Default Structs maintain the default behavior of structures in C#, which means that there is no change in the way structures are treated at the default value level.
– If a structure has fields that must be initialized with values other than the default values, a custom constructor must still be provided.
Comparison Example
Before C# 12, to get a custom initialization, you had to do something like this:
public struct Punto
{
public int X;
public int Y;
public Punto(int x, int y)
{
X = x;
Y = y;
}
}
With Auto-Default Structs, you can simply declare the structure without worrying about the default constructor to get proper initialization:
Point p = new Point(); // X = 0, Y = 0
Conclusion
Auto-Default Structs in C# 12 further improve the usability of structures by eliminating the need to define default constructors and ensuring that all structures are initialized in a safe and predictable manner. This functionality reduces boilerplate code and makes the language more streamlined and intuitive for developers, while improving readability and code maintenance.
REQUIRED MEMBERS
The Required Members feature in C# 12 was introduced to improve code safety and ensure that objects are fully and properly initialized before they are used. This feature allows certain properties or fields of a class or structure to be declared as required, i.e., mandatory, and therefore must necessarily be initialized when the object is created.
What is a Required Member?
A Required Member is a property or field that must be initialized when an instance of a class or structure is created. If a member is declared as required and is not initialized in the object’s constructor or initializer, the compiler will generate an error, thus ensuring that the member is always set.
Syntax of Required Members
To declare a member as required, the required keyword is used before the member declaration. Here is an example:
public class Persona
{
public required string Nome { get; set; }
public required int Età { get; set; }
public string? Indirizzo { get; set; }
}
In this example, Name and Age are declared as required, which means they must be initialized when creating an instance of the Person class. Address, on the other hand, is optional.
Example Usage
var person = new Person
{
Name = “Mario.”
Age = 30
};
This code is correct because both required members (Name and Age) have been initialized.
Benefits of Required Members.
1. Full Initialization: Ensures that all essential properties of an object are initialized correctly, preventing runtime errors due to null or uninitialized values.
2. Code Safety: Reduces the possibility of errors and bugs by forcing full initialization of objects.
3. Readability: Makes explicit which properties are essential to the object, improving code understanding.
Considerations
-Required Members must be initialized directly in the constructor, in the object initializer, or through a specific constructor.
-This feature is particularly useful in situations where certain members are expected to always be present and properly set for the object to function properly.
Example with Constructor
Required members can also be initialized through a constructor:
public class Persona
{
public required string Nome { get; set; }
public required int Età { get; set; }
public string? Indirizzo { get; set; }
public Persona(string nome, int età)
{
Nome = nome;
Età = età;
}
}
var persona = new Persona(“Mario“, 30);
In this example, the Persona constructor initializes the required members, satisfying the requirements imposed by the compiler.
Conclusion
The Required Members feature in C# 12 represents a major step forward in code safety and robustness, ensuring that all critical members of an object are always initialized. This makes code safer, more readable, and easier to maintain, preventing common errors related to incomplete object initialization.
PRIMARY CONSTRUCTORS IN C# 12
Primary constructors in C# 12 represent a new feature introduced to simplify the creation of classes and structures, making code more concise and easier to read. Primary constructors allow constructor parameters to be defined directly in the class or structure declaration, which can then be used to initialize fields or properties.
Persona p = new Persona("Mario", eta: 40); Console.WriteLine(p); public class Persona(string nome, int eta) { public string Nome { get; } = nome; public int Età { get; } = eta; // Metodi o proprietà aggiuntivi possono essere aggiunti qui public override string ToString() { return Nome + " " + Età; } }
In this example, Person has a primary constructor that takes two parameters: name and age. These parameters are then used to initialize the Name and Age properties.
Example 2: Primary Constructor in a Structure
public struct Punto(int x, int y)
{
public int X { get; } = x;
public int Y { get; } = y;
// Metodi o proprietà aggiuntivi possono essere aggiunti qui
}
Example 3: Primary Constructor with Additional Initialization
public class Impiegato(string nome, int età)
{
public string Nome { get; } = nome;
public int Età { get; } = età;
public decimal Stipendio { get; }
public Impiegato(string nome, int età, decimal stipendio) : this(nome, età)
{
Stipendio = stipendio;
}
}
In this example, Employee has a primary constructor that initializes Name and Age, and an additional constructor that sets the Salary property.
Advantages of Primary Constructors.
1. Conciseness: Reduces boilerplate code by combining constructor and property declarations.
2. Readability: Makes code easier to read and understand.
3. Immutability: Encourages the use of readonly properties, which can help make objects immutable.
Limitations and Considerations.
-Primary constructors are primarily syntactic sugar and do not introduce new functionality beyond that already obtainable with traditional constructors.
-They may not be suitable for all scenarios, especially where more complex initialization logic is required.
Conclusion
Primary constructors in C# 12 are a powerful addition to the language, making it easier to create and initialize objects with less code. They improve readability and maintainability, aligning with modern C# trends toward more concise and expressive syntax.
DEFAULT LAMBDA PARAMETER
In C# 12, the ability to specify default parameters in lambda expressions has been introduced. This feature allows a default value to be assigned to the parameters of a lambda, making the code more flexible and concise.
What is a Default Parameter in a Lambda?
A default parameter in a lambda is a value that is automatically used if no explicit value is provided when the lambda is invoked. This concept is similar to that of default parameters in methods, but applied to lambda expressions.
Example of Lambda with Default Parameter
Func<int, int, int> somma = (int a, int b = 10) => a + b;
int risultato1 = somma(5); // Utilizza il valore di default per ‘b’, risultato = 15
int risultato2 = somma(5, 20); // Utilizza il valore esplicito fornito per ‘b’, risultato = 25
In this example:
– The lambda sum has two parameters: a and b.
– Parameter b has a default value of 10.
-When sum is invoked with only one argument (5), b automatically takes the value of 10.
– When sum is invoked with two arguments (5 and 20), b takes the value of 20.
Advantages of using Default Parameters in Lambdas.
1. Flexibility: Allows the use of lambdas to be simplified when it is not necessary to always specify all parameters.
2. Conciseness: Reduces repetitive code and improves readability by avoiding the need to define multiple separate overloads or lambdas to handle different cases.
3. Maintainability: Allows common scenarios to be handled with less code, making it easier to make changes in the future.
Considerations
– This feature is especially useful when working with lambdas that have optional parameters and you want to avoid creating multiple versions of the same lambda.
– It is important to remember that, as with default parameters in methods, parameters with default values should be placed at the end of the parameter list.
Conclusion
Default parameters in lambdas in C# 12 are an addition that increases the flexibility of the language, making it easier and clearer to use lambda expressions. This allows developers to write more concise code and handle common scenarios with less complexity.
COLLECTION EXPRESSION
Collection Expressions in C# 12 represent a new feature that simplifies the creation and initialization of collections (such as lists, arrays, sets, etc.) in a more concise and readable way. This feature allows you to express the creation of a collection using a syntax similar to lambda expressions or object initializers, reducing the need for boilerplate code.
What is a Collection Expression?
A Collection Expression is a compact and straightforward way to create and initialize a collection in C#. It uses a special syntax that allows collection elements to be specified in much the same way as is used to define arrays or anonymous objects.
Example of Collection Expression
var numbers = [1, 2, 3, 4, 5];
In this example, numbers is a list of integers created using a Collection Expression. The syntax [] was introduced to allow the elements of the collection to be specified directly within the square brackets.
Advanced Usage
Collection Expressions can also be used with other types of collections, such as dictionaries or sets.
Example with a Dictionary
var dictionary = [“key1” => “value1“, “key2” => “value2“];
In this example, dictionary is a dictionary created and initialized using a Collection Expression. The => syntax is used to map keys to values.
Example with a List and Complex Operations.
var list = [1, 2, 3, 4, 5].Where(x => x % 2 == 0).ToList();
Here, a list of numbers is initialized and then filtered using LINQ to get only even numbers, all in a single expression.
Advantages of Collection Expressions.
1. Conciseness: Allows you to initialize collections with a much shorter syntax than traditional methods.
2. Readability: Code is more readable and straightforward, reducing visual noise due to explicit calls to collection constructors.
3. Flexibility: Can be used in conjunction with other C# features such as LINQ, making operations on collections smoother.
Considerations
– Collection Expressions are designed to improve syntax, but it is important to use them appropriately to maintain code readability.
– This feature integrates well with other features of the language, such as target-typed expressions, where the collection type is inferred from the context.
Conclusion
Collection Expressions in C# 12 provide a more elegant and concise way to work with collections, reducing verbosity and increasing code readability. This feature represents another step forward in modernizing C# syntax, aligning it with developers’ needs to write clean, efficient code.
Leave A Comment