-
Notifications
You must be signed in to change notification settings - Fork 0
CSharp
- CSharp
- Operator
- Filters
- Ref and Out
- Boxing and Unboxing
- DI (Dependency Injection)
- What are the features of C#?
- What is Asynchronous and synchronous with example
- Delegates and Events
- Method signature
- Comments
- Indexer
- Diff b/w const, static and readonly
- Lazy Loading
- Concurrency & Parallelism
- Diff b/w Interface and Abstract class
- Diff b/w list and arrayList
- Diff b/w IEnumerable, ICollection and IList
- Diff b/w Dynamic, Object and Reflection
- Diff b/w MetData and Manifest
Filters allow us to run custom code before or after executing the action method. They provide ways to do common repetitive tasks on our action method. The filters are invoked on certain stages in the request processing pipeline.
- Authorization FIlter
- Resource Filter
- Action Filter
- Result Filter
- Exception Filter
- by action method,
- by controller class or
- globally (which be applied to all the controller and actions).
We need to register filters in to the MvcOption.Filters collection within ConfigureServices method.
- ServiceFilterAttribute
[ServiceFilter(typeof(ExampleFilterWithDI))]
public IActionResult Index()
{
return View();
}
- TypeFilterAttribute
- It is very similar to ServiceFilterAttribute and also implemented from IFilterFactory interface.
- The "TypeFilterAttribute" can be optionally accept constructor arguments for the type.
[TypeFilter(typeof(ExampleFilterAttribute), Arguments = new object[] {"Argument if any" })]
public IActionResult About()
{
return View();
}
- IFilterFactory implemented on attribute
References:
-
ref tells the compiler that the object is initialized before entering the function, while out tells the compiler that the object will be initialized inside the function.
-
So while ref is two-ways, out is out-only.
Ref | Out |
---|---|
The parameter or argument must be initialized first before it is passed to ref. | It is not compulsory to initialize a parameter or argument before it is passed to an out. |
It is not required to assign or initialize the value of a parameter (which is passed by ref) before returning to the calling method. | A called method is required to assign or initialize a value of a parameter (which is passed to an out) before returning to the calling method. |
Passing a parameter value by Ref is useful when the called method is also needed to modify the pass parameter. | Declaring a parameter to an out method is useful when multiple values need to be returned from a function or method. |
It is not compulsory to initialize a parameter value before using it in a calling method. | A parameter value must be initialized within the calling method before its use. |
When we use REF, data can be passed bi-directionally. | When we use OUT data is passed only in a unidirectional way (from the called method to the caller method). |
Both ref and out are treated differently at run time and they are treated the same at compile time. |
References:
-
Boxing
- The conversion of value type to reference type is known as boxing.
-
Unboxing
- The conversion of reference type to value is known as unboxing.
References:
- It is a design pattern that allows objects to depend on other objects, called dependencies, without creating them directly.
- It is a software design pattern which enables the development of loosely coupled code. Through DI, you can decrease tight coupling between software components. It is also known as Inversion-of-Control.
Constructor Injection
Setter Injection
Method Injection
- Transient Service
- New instance transient service is created whenever the service is requested.
- Scoped Service
- Created once per scope; i.e., web request.or any unit of work.
- Singleton Service
- Singleton service is only created when it is called for the first time. In the next subsequent requests, the same instance is provided.
Be careful, while injecting service into another service with a different lifetime
Consider the example of Singleton Service, which depends on another Service which is registered with say the transient lifetime.
When the request comes for the first time a new instance of the singleton is created. It also creates a new instance of the transient object and injects into the Singleton service.
When the second request arrives the instance of the singleton is reused. The singleton already contains the instance of the transient service Hence it is not created again. This effectively converts the transient service into the singleton.
The services with the lower lifetime injected into service with a higher lifetime would change the lower lifetime service to a higher lifetime. This will make debugging the application very difficult and should be avoided at all costs.
Hence, remember the following rules
- Never inject Scoped & Transient services into Singleton service.
- Never inject Transient services into scoped service
References
- https://stackify.com/dependency-injection-c-sharp/
- https://www.tektutorialshub.com/asp-net-core/asp-net-core-dependency-injection-lifetime/
-
Object-Oriented Programming
C# is an object-oriented programming (OOP) language. It supports features such as classes, objects, encapsulation, inheritance, and polymorphism.
-
Type Safety
It is a type-safe language, which means that it enforces type checking at compile time to ensure that variables are used only in the ways intended by the programmer. Example:-
int x = 10; // declaring an integer variable and initializing it with value 10 string str = "Hello, World!"; // declaring a string variable and initializing it with a string value // We cannot assign a string value to an integer variable: x = str; // This will result in a compilation error // Similarly, we cannot call a method on a variable that doesn't support it: int y = 5; y.ToUpper(); // This will result in a compilation error since ToUpper() method
-
Garbage Collection
C# includes automatic garbage collection, which automatically frees up memory that is no longer being used by the program.
-
Cross-Platform Support
C# can be used to develop applications for a wide range of platforms, including Windows, macOS, Linux, and mobile devices.
-
Language Interoperability
C# can interoperate with other programming languages, including C, C++, and Visual Basic.
-
LINQ
Language Integrated Query (LINQ) is a powerful feature in C# that allows developers to query and manipulate data from different data sources using a uniform syntax.
-
Asynchronous Programming
C# includes support for asynchronous programming, which allows developers to write code that can execute concurrently without blocking the main thread.
-
Exception Handling
C# includes robust exception handling capabilities that allow developers to handle and recover from runtime errors in a structured manner.
-
Delegates and Events
C# supports the use of delegates and events, which are used for implementing callback functions and event-driven programming.
-
Security Features
C# includes security features such as code access security, which helps protect against malicious code execution, and cryptography, which allows for secure communication and data storage.
-
Synchronous refers to an operation that blocks the execution of the program until the operation completes
-
Asynchronous, on the other hand, refers to an operation that does not block the execution of the program but instead executes in the background, allowing the program to continue executing other operations.
public static void Main()
{
Console.WriteLine("Starting synchronous operation...");
string result = GetDataFromWeb(); // This method blocks the execution until it gets the data
Console.WriteLine("Synchronous operation completed with result: " + result);
}
public static string GetDataFromWeb()
{
// This method makes a web request to get some data
WebClient client = new WebClient();
string result = client.DownloadString("http://example.com");
return result;
}
In the above code, the
Main
method makes a call to theGetDataFromWeb
method, which makes a web request to retrieve some data. Since this operation is synchronous, the execution of the program is blocked until the web request completes and returns the data. Only then does the program continue to execute the next line of code.
public static async Task Main()
{
Console.WriteLine("Starting asynchronous operation...");
Task<string> resultTask = GetDataFromWebAsync(); // This method executes in the background
Console.WriteLine("Asynchronous operation started, program continues executing other operations...");
// Do some other work here while the GetDataFromWebAsync() method is executing in the background
string result = await resultTask; // This line blocks until the GetDataFromWebAsync() method completes and returns the data
Console.WriteLine("Asynchronous operation completed with result: " + result);
}
public static async Task<string> GetDataFromWebAsync()
{
// This method makes an asynchronous web request to get some data
HttpClient client = new HttpClient();
string result = await client.GetStringAsync("http://example.com");
return result;
}
In the above code, the
Main
method makes a call to theGetDataFromWebAsync
method, which makes an asynchronous web request to retrieve some data. Since this operation is asynchronous, the execution of the program is not blocked and the program continues executing other operations while the web request is being made in the background. The program then waits for the result of the asynchronous operation using theawait
keyword, which does not block the execution of the program but instead waits for the result to become available before continuing to execute the next line of code.
- A
delegate
is a type that represents a reference to a method with a particular parameter list and return type
public delegate int BinaryOperation(int x, int y);
public class Calculator
{
public int Add(int x, int y)
{
return x + y;
}
public int Subtract(int x, int y)
{
return x - y;
}
}
public static void Main()
{
Calculator calculator = new Calculator();
BinaryOperation operation = new BinaryOperation(calculator.Add);
int result = operation(5, 3); // result = 8
operation = new BinaryOperation(calculator.Subtract);
result = operation(5, 3); // result = 2
}
- An
event
is a construct built on top of delegates that allows objects to be notified when an event occurs.
public class Button
{
public event EventHandler Clicked;
public void Click()
{
if (Clicked != null)
{
Clicked(this, EventArgs.Empty);
}
}
}
public static void Main()
{
Button button = new Button();
button.Clicked += OnButtonClicked;
button.Click();
}
public static void OnButtonClicked(object sender, EventArgs e)
{
Console.WriteLine("Button clicked!");
}
- The signature of a method in C# consists of its name and the types of its parameters. The return type of the method is not considered part of its signature.
In c#, there are three types of comments available, those are
- Single-line Comments
// Single Line Comment
- Multi-line Comments
/* Multi Line Comment */
- XML Comments
///<summary>
/// This class does something.
///</summary>
- It allows instances of a class or structure to be indexed same like an array.
public class UserIndexer
{
public Dictionary<string, string> Users = new Dictionary<string, string>();
public string this[string index]
{
get { return Users[index]; }
set { Users[index] = value; }
}
}
UserIndexer user = new();
user["Name"] = "Keshav";
user["password"] = "1234";
Console.WriteLine($"Name: {user["Name"]}");
Console.WriteLine($"Password: {user["password"]}");
/*Output:
Name: Keshav
Password: 1234
*/
public class UserIndexer
{
public Dictionary<string, string> Users = new();
public Dictionary<char, int> Chars = new();
public string this[string index]
{
get { return Users[index]; }
set { Users[index] = value; }
}
public int this[char index]
{
get { return Chars[index]; }
set { Chars[index] = value; }
}
}
UserIndexer user = new();
user["Name"] = "Keshav";
user["password"] = "1234";
user['A'] = 65;
user['C'] = 67;
Console.WriteLine($"Name: {user["Name"]}");
Console.WriteLine($"Password: {user["password"]}");
Console.WriteLine($"A ASCII value {user['A']}");
Console.WriteLine($"C ASCII value {user['C']}");
/*Output:
Name: Keshav
Password: 1234
A ASCII value 65
C ASCII value 67
*/
Following are the important points which we need to remember about indexers in c#.
- In c#, the indexer is a special type of property, allowing instances of a class or structure to be indexed same as an array.
- In c#, the indexer is same as the property, but the only difference is, the indexer will define with this keyword along with the square bracket and parameters.
- Indexers can be overloaded by having different signatures.
- Indexers cannot be a static member as it’s an instance member of the class.
- Passing indexer value as a ref or out parameter is not supported.
In C#, indexers are instance members that allow objects of a class to be indexed in a similar way as arrays. They cannot be declared as static because they are used to access or modify the data of individual instances of a class, not shared data across all instances.
References
Modifier | Scope | Lifetime | Assigned At | Can Be Static | Can Change After Assignment | Access Through | Typical Use Case |
---|---|---|---|---|---|---|---|
const |
Class | Application | Declaration (Compile Time) | Implicitly (Always) | No | Class Name | Primitive/enum values known at compile time |
readonly |
Instance/Class | Object/Application | Constructor or Declaration (Runtime) | Explicitly (when combined with static ) |
No | Object Reference or Class Name | Values computed at runtime but immutable after construction |
static |
Class | Application | Constructor, Field Initializer, or Any Method (Runtime) | Yes (Inherently) | Yes (unless combined with readonly ) |
Class Name | Shared data or methods that do not require an instance of the class |
More info
In C#, const
, readonly
, and static
are modifiers that can be applied to fields and properties to control their behavior and accessibility. Here's a breakdown of their differences:
-
const
(Constant):- A
const
field is a compile-time constant, meaning its value is set at compile time and cannot be changed. - It is implicitly static, and you must assign a value to it at the time of its declaration.
- The value of a
const
field is the same across all instances of the class, and you access it using the class name rather than an instance of the class. - It can only be used with primitive types (such as
int
,double
,string
, etc.) and enum types.
- A
Example:
public class MyClass
{
public const int MyConstant = 42;
}
// Usage: int value = MyClass.MyConstant;
-
readonly
(Read-only):- A
readonly
field can be assigned only during initialization or in the constructor of the class in which it is declared. - It is not static by default (but can be made static explicitly), meaning its value can be different across instances of the class.
- If you declare a
readonly
field as static, its value will be the same for all instances, just like aconst
, but it can be assigned a value that is computed at runtime, unlike aconst
.
- A
Example:
public class MyClass
{
public readonly int MyReadOnlyField;
public MyClass(int value)
{
MyReadOnlyField = value; // Assignment is allowed only here or in a field initializer
}
}
// Usage: MyClass myClass = new MyClass(10); int value = myClass.MyReadOnlyField;
-
static
:- A
static
field belongs to the type itself rather than to any specific instance of the class. - A
static
field is initialized once and retains its value between calls. -
static
fields can be modified at any time, unless they are also marked asreadonly
.
- A
Example:
public class MyClass
{
public static int MyStaticField = 0;
}
// Usage: MyClass.MyStaticField = 10; int value = MyClass.MyStaticField;
In summary:
-
const
is for compile-time constants, with values known at compile time and unchangeable at runtime. -
readonly
is for fields that should not be changed after the constructor has finished executing, but their initial values can be computed at runtime. -
static
means the field is associated with the type rather than with any instance, and it can be modified unless it's combined withreadonly
.
-
Lazy loading is basically loading data when its required, not at the time when the object instance is created, the lazy loading technique can help boosting application performance some extent if used correctly.
-
Example
Think of real-time business scenario, when you want to create a client object along with client order list, now in general you probably will have order list property in client object, so when a new instance of client object is created, the order list also will be loaded at the same time. If the order list is long, that will take longer the execution time, when you may not need those data while accessing other client class property, which can be avoided by using lazy class, you can load the data only when the data is required.
public class data
{
public static List<int> GetList1()
{
List<int> list1 = new List<int>();
for (int i = 0; i <= 5000000; i++)
{
list1.Add(i);
}
return list1;
}
public static Lazy<List<int>> GetList3()
{
Lazy<List<int>> list3 = new Lazy<List<int>>();
for (int i = 0; i <= 5000000; i++)
{
list3.Value.Add(i);
}
return list3;
}
}
You will be surprised to see the execution time; the lazy list took half time to perform than the normal list object.
- Concurrency refers to the ability of an application to deal with multiple tasks at the same time. Concurrency does not necessarily mean that these tasks are actually running simultaneously; it means that the tasks can start, run, and complete in overlapping time periods, and the architecture is such that it can handle multiple tasks at once. Concurrency is about structure.
using System;
using System.Threading;
using System.Threading.Tasks;
class ConcurrencyExample
{
static void Main()
{
Task task1 = Task.Run(() => DoTask("Task 1"));
Task task2 = Task.Run(() => DoTask("Task 2"));
Task.WaitAll(task1, task2);
}
static void DoTask(string name)
{
for (int i = 0; i < 5; i++)
{
Thread.Sleep(1000); // Simulate work
Console.WriteLine($"{name} - iteration {i}");
}
}
}
- Parallelism is about execution. It refers to the simultaneous execution of multiple tasks or processes. This requires a system with multiple processing units (cores). Parallelism is possible when tasks are independent or can be broken into independent subtasks which can be processed simultaneously.
using System;
using System.Threading.Tasks;
class ParallelismExample
{
static void Main()
{
Parallel.Invoke(
() => DoTask("Task 1"),
() => DoTask("Task 2")
);
}
static void DoTask(string name)
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"{name} - iteration {i}");
}
}
}
Feature | Interface | Abstract Class |
---|---|---|
Instantiation | Cannot be instantiated. | Cannot be instantiated. |
Implementation | Cannot have any implementation, only method signatures. | Can have both abstract methods and concrete methods. |
Fields | Cannot have fields. | Can have fields. |
Constructors/Destructors | Cannot have constructors or destructors. | Can have constructors and destructors. |
Access Modifiers | Members are implicitly public. | Members can have various access modifiers. |
Inheritance | A class can implement multiple interfaces. | A class can inherit from only one abstract class. |
Default Behavior | Cannot provide default behavior. | Can provide default behavior. |
Property Definitions | Can define properties, but not their accessors. | Can define properties with accessors. |
Multiple Inheritance | Supports multiple inheritance (a class can implement multiple interfaces). | Does not support multiple inheritance (a class can inherit only one abstract class). |
Versioning | Adding new members to an interface breaks existing implementers. | New methods can be added as abstract or virtual, allowing subclasses to optionally override them. |
Purpose | Defines a contract that implementing classes must follow. | Serves as a base class to define common behavior and provide a common structure. |
Feature | List<T> |
ArrayList |
---|---|---|
Type Safety | Type-safe, stores elements of a specific type. | Not type-safe, stores elements of any type (object). |
Performance | Generally better performance due to type-safety. | Can suffer performance penalties due to boxing and unboxing when storing value types. |
Generics | Supports generics. | Does not support generics. |
Namespace | System.Collections.Generic | System.Collections |
Requires Casting | No casting needed when retrieving elements. | Requires casting when retrieving non-object types. |
Methods and Properties | Provides a range of methods and properties designed for generic collections. | Provides methods and properties common to all non-generic collections. |
Introduced in Version | .NET 2.0 (with generics) | .NET 1.0 (before generics were introduced) |
Null Values | Can store null if the type T is a reference type. | Can store null. |
Capacity Management | Automatically resizes. Has a Capacity property to manage the underlying array size. |
Automatically resizes. Has a Capacity property to manage the underlying array size. |
Sorting | Provides a Sort method that works with IComparer<T> or Comparison delegates.
|
Provides a Sort method that requires an IComparer implementation. |
Thread-Safety | Not thread-safe (like most collection classes). Use Concurrent collections for thread-safe operations. |
Not thread-safe. |
In C#, ICollection
, IEnumerable
, and IList
are interfaces that represent different levels of collection functionality in the System.Collections and System.Collections.Generic namespaces.
Here's a brief overview of each, along with their differences:
-
IEnumerable
(orIEnumerable<T>
for generics):
- This is the most basic interface of the three. It allows the implementing collection to be enumerated, which means you can iterate through the collection using a
foreach
loop. - It defines a single method,
GetEnumerator()
, which returns anIEnumerator
(orIEnumerator<T>
for generics) that provides the ability to iterate through the collection. - All collections that can be iterated over implement this interface.
-
ICollection
(orICollection<T>
for generics):
- This interface extends
IEnumerable
and represents a general collection that can be enumerated. -
ICollection
provides additional functionality, such as the ability to get the count of elements (Count
property), add or remove items from the collection (if not read-only), and check if the collection contains a specific item. - It also includes methods for copying the elements to an array (
CopyTo
) and getting a value indicating whether the collection is read-only.
-
IList
(orIList<T>
for generics):
-
IList
extendsICollection
and represents a collection of objects that can be individually accessed by index. - It provides functionality for adding, removing, and inserting items at a specified index, as well as retrieving items by their index.
-
IList
also has anIndexOf
method that returns the index of a specific item in the list, and anIsFixedSize
property indicating whether the list has a fixed size.
In summary:
-
IEnumerable
is for iteration. -
ICollection
adds counting, adding, and removing capabilities, as well as the ability to test for containment and to copy the collection to an array. -
IList
includes all of the above, plus the ability to manipulate the collection by index.
Feature/Method | IEnumerable | ICollection | IList |
---|---|---|---|
Enumeration | Yes | Yes | Yes |
Counting Elements | No | Yes | Yes |
Adding Elements | No | Yes* | Yes* |
Removing Elements | No | Yes* | Yes* |
Clearing Collection | No | Yes* | Yes* |
Contains | No | Yes | Yes |
Copy To Array | No | Yes | Yes |
Read-Only Collection | No | Yes | Yes |
Access by Index | No | No | Yes |
Insert at Index | No | No | Yes* |
Remove at Index | No | No | Yes* |
Index Of | No | No | Yes |
Fixed Size | No | No | Yes** |
Aspect | Dynamic | Object | Reflection |
---|---|---|---|
Type checking | At runtime | At compile time (unless casting) | At runtime |
Performance | Slower (runtime type checking) | Faster (statically typed) | Slower (overhead of metadata) |
Complexity | Simpler (easy syntax) | Simple (base type for all objects) | More complex (API understanding) |
Control | Less control (runtime binding) | Moderate control (type casting) | More control (detailed type manipulation) |
Error detection | At runtime | At compile time and runtime (when casting) | Mostly at runtime |
Use Cases | Interop with dynamic languages, handling unknown types at compile time | General OOP, storing mixed types in non-generic collections | Dynamic type creation and manipulation, advanced scenarios like plugin architectures |
Aspect | Metadata | Manifest |
---|---|---|
Definition | Information describing the types, members, and references in an assembly. | Part of the metadata containing assembly identity and file list. |
Contains | - Type definitions - Member signatures - Custom attributes - Security information |
- Assembly's identity (name, version, culture) - File list - Assembly references - Assembly-level attributes |
Used by | - CLR for loading and execution - Tools for reflection and inspection |
- CLR for resolving assembly dependencies - Versioning and compatibility checks |
Location | Stored in metadata tables within the assembly. | A specific section within the metadata of the assembly. |
Accessibility | Can be accessed using reflection in code. | Can be viewed using tools like ILDASM or reflection in code. |
Example |
Type.GetTypeInfo() , MemberInfo[] members = type.GetMembers() |
AssemblyName name = assembly.GetName() |
Imagine a .NET assembly as a book. The book represents the compiled code, which is the assembly.
-
Metadata is like the detailed table of contents and index of the book. It includes information about every chapter, section, and significant term used in the book. In the context of an assembly, these would be the classes, methods, properties, fields, and other types, as well as their signatures and attributes.
-
The Manifest is like the summary page of the book that contains the book's title, edition, a list of authors, and the ISBN. In terms of an assembly, this includes the assembly's name, version, culture, and other assembly-level information.
Here's a code example to illustrate how you might encounter metadata and manifest information in a .NET Core application.
using System;
using System.Reflection;
// Define a simple class with some metadata (class name, method, property)
public class ExampleClass
{
public string ExampleProperty { get; set; }
public void ExampleMethod()
{
Console.WriteLine("This is an example method.");
}
}
class Program
{
static void Main(string[] args)
{
// Getting the assembly of the ExampleClass
Assembly assembly = typeof(ExampleClass).Assembly;
// Accessing the Manifest: Assembly Name
Console.WriteLine("Assembly Name: " + assembly.GetName().Name);
// Accessing the Manifest: Version
Console.WriteLine("Version: " + assembly.GetName().Version);
// Accessing the Metadata: Types
Type[] types = assembly.GetTypes();
foreach (var type in types)
{
Console.WriteLine("Type: " + type.Name);
// Accessing the Metadata: Members (methods, properties, fields, etc.)
MemberInfo[] members = type.GetMembers();
foreach (var member in members)
{
Console.WriteLine("Member: " + member.Name);
}
}
}
}