- In comparing two values, the .NET Framework differentiates the concepts of equality comparison and order comparison.
- Equality comparison tests whether two instances are semantically the same.
- Order comparison tests which of two (if any) instances comes first when arranging them in ascending or descending sequence.
- For string Equality comparison, you can use the
- == Operator
- string.Equals() static method as will as instance method, this option is better because they allow you to specify options such as case-sentitvity/culture-sensitivity.
- For string Order comparison, you can use the
- CompareTo() instance method
- string.Compare() static method
- stirng.CompareOrdinal() static method
- The return values of above said methods will be
- 0 for equal
- 1 if first value comes before second value
- 2 if second value comes before first value
Learn easy - C#
Framework Fundamentals - String - Comparing Strings
Framework Fundamentals - String
- A C# string represents a sequence of Unicode Characters and it is defined in System namespace as System.string class.
- String is immutable.
- Construction of String
//Method 1 - using Literals
string s1 = "Sample string";
//Method 2 - repeating sequence of characters with string Constructor
string s2 = new string('*', 10); // **********
//Method 3 - using Character array
char[] charArray = "Hello".ToCharArray();
string s3 = new string(charArray); // Hello
string s1 = "Sample string";
//Method 2 - repeating sequence of characters with string Constructor
string s2 = new string('*', 10); // **********
//Method 3 - using Character array
char[] charArray = "Hello".ToCharArray();
string s3 = new string(charArray); // Hello
- Null and empty strings
- An empty string has a length of zero.
- To create an empty string, you can use either a literal("") or the static string.Empty field;
- Because strings are reference types, they can also be null.
- The static string.IsNullOrEmpty method is a useful shortcut for testing whether a given string is either null or empty.
- Accessing characters within a string
//Method 1 - using Indexer
string s1 = "Hello";
char charAtIndexOne = s1[1]; // e
//Method 2 - using foreach
foreach (char item in s1)
{
Console.Write($"{item},"); // H,e,l,l,o,
}
string s1 = "Hello";
char charAtIndexOne = s1[1]; // e
//Method 2 - using foreach
foreach (char item in s1)
{
Console.Write($"{item},"); // H,e,l,l,o,
}
- Searching within strings
- string class provides instance method for searching within strings
- Take Note: all string methods are case-sensitive and culture-sensitive.
string s1 = "Hello Mr. X";
Console.WriteLine(s1.Contains("x")); //False - as it is smaller case
Console.WriteLine(s1.StartsWith("HE")); //False
Console.WriteLine(s1.StartsWith("He")); //True - as it is matches case
Console.WriteLine(s1.EndsWith("x")); //False -
Console.WriteLine(s1.IndexOf("X")); //10
Console.WriteLine(s1.IndexOf("M",7)); // -1 , it searches from index 7 i.e character r in the string
// so it cannot find and returns -1
Console.WriteLine(s1.IndexOf("l")); //2 searches from first character of the string i.e H
Console.WriteLine(s1.LastIndexOf("l")); //3 searches from last character of the string i.e X
Console.WriteLine(s1.LastIndexOf("l", 1)); // -1 , it searches from index 1 i.e character e in the string
Console.WriteLine(s1.IndexOfAny("abcde".ToCharArray())); // searches for either a or b or c or d or e
//1 - found e in index 1
Console.WriteLine(s1.LastIndexOfAny("abcde".ToCharArray())); // searches for either a or b or c or d or e
//Search from last character
//1 - found e in index 1
Console.WriteLine(s1.IndexOfAny("Xabcde".ToCharArray())); // searches for either X or a or b or c or d or e
//1 - found e in index 1
//Searches from first character
Console.WriteLine(s1.LastIndexOfAny("Xabcde".ToCharArray())); // searches for either a or b or c or d or e
//Search from last character
//10 - found e in index 1
Console.WriteLine(s1.Contains("x")); //False - as it is smaller case
Console.WriteLine(s1.StartsWith("HE")); //False
Console.WriteLine(s1.StartsWith("He")); //True - as it is matches case
Console.WriteLine(s1.EndsWith("x")); //False -
Console.WriteLine(s1.IndexOf("X")); //10
Console.WriteLine(s1.IndexOf("M",7)); // -1 , it searches from index 7 i.e character r in the string
// so it cannot find and returns -1
Console.WriteLine(s1.IndexOf("l")); //2 searches from first character of the string i.e H
Console.WriteLine(s1.LastIndexOf("l")); //3 searches from last character of the string i.e X
Console.WriteLine(s1.LastIndexOf("l", 1)); // -1 , it searches from index 1 i.e character e in the string
Console.WriteLine(s1.IndexOfAny("abcde".ToCharArray())); // searches for either a or b or c or d or e
//1 - found e in index 1
Console.WriteLine(s1.LastIndexOfAny("abcde".ToCharArray())); // searches for either a or b or c or d or e
//Search from last character
//1 - found e in index 1
Console.WriteLine(s1.IndexOfAny("Xabcde".ToCharArray())); // searches for either X or a or b or c or d or e
//1 - found e in index 1
//Searches from first character
Console.WriteLine(s1.LastIndexOfAny("Xabcde".ToCharArray())); // searches for either a or b or c or d or e
//Search from last character
//10 - found e in index 1
Framework Fundamentals - Char
- A C# char represents a single Unicode Character and it is defined in System namespace as System.char struct.
- System.Char defines a range of static methods for working with characters, such as ToUpper(), ToLower(), and IsWhiteSpace(), please refer the table below for reference.
- As you can refer from the last column of the table above, all the characters are categorized, if a character does not fall under any of categories given above, then it is considered as invalid character.
- All these methods are culture-sensitive.
- To check a character validity you can call static method char.GetUnicodeCategory(), if the result is UnicodeCategory.OtherNotAssigned, then character is invalid.
- Example
char a = 'a';
Console.WriteLine(char.GetUnicodeCategory(a).ToString()); //LowercaseLetter
Console.WriteLine(char.GetUnicodeCategory(a).ToString()); //LowercaseLetter
- By default, a Char is 16 bits wide, and it is enough to represent all normal Unicode characters(called as Basic Multilingual Plane), Some special characters may need extra space, we will see about that in later posts.
When to use lock(this) and when not to use lock(this)
- Before we see when to use and not use lock(this).
- First let's see when to use locks
- For explaining refer the below given code
class JointAccount
{
int Balance = 10000;
public void WithDraw(int Amount)
{
if (Amount <= Balance)
{
Console.WriteLine("Balance before withdraw: {0}", Balance);
Balance = Balance - Amount;
Console.WriteLine("Balance after withdraw: {0}", Balance);
}
else
{
Console.WriteLine("Cannot withdraw, as amount exceeds balance");
}
}
}
class Program
{
static void Main(string[] args)
{
JointAccount jointAccount = new JointAccount(); //Joint Account of 10 members
for (int i = 0; i < 10; i++)
{
new Thread(() => jointAccount.WithDraw(10000)).Start();
}
Console.ReadLine();
}
}
{
int Balance = 10000;
public void WithDraw(int Amount)
{
if (Amount <= Balance)
{
Console.WriteLine("Balance before withdraw: {0}", Balance);
Balance = Balance - Amount;
Console.WriteLine("Balance after withdraw: {0}", Balance);
}
else
{
Console.WriteLine("Cannot withdraw, as amount exceeds balance");
}
}
}
class Program
{
static void Main(string[] args)
{
JointAccount jointAccount = new JointAccount(); //Joint Account of 10 members
for (int i = 0; i < 10; i++)
{
new Thread(() => jointAccount.WithDraw(10000)).Start();
}
Console.ReadLine();
}
}
- It is Joint Account, so any one can have access to the account can withdraw amount from the Joint Account.
- As per the above example Single Instance of JointAccount class is accessed by 10 threads almost at the same time
- But anyhow, the balance shouldn't go negative, they cannot withdraw the money equal to the balance that they have in their account
- But the above example will allow to violate the above said condition
- Take Note: In a multi-threaded environment you cannot expect the same result all the times like single threaded, because of some times it may produce correct result, that doesn't mean it will always, so run your program multiple time to confirm you code is working fine.
- So the above code result may be like this
Balance before withdraw: 10000
Balance after withdraw: 0
Balance before withdraw: 10000
Balance after withdraw: -10000
Balance before withdraw: 10000
Balance after withdraw: -20000
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Balance after withdraw: 0
Balance before withdraw: 10000
Balance after withdraw: -10000
Balance before withdraw: 10000
Balance after withdraw: -20000
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
- So here, in this situation we need locking, because the code allows for balance to go negative
- So now, that we all know a variable can be used as a locker, it should be of reference type.
- And the locker variable should be private member of that class.
- Why it should be a private member, why does it cannot be public?
- A locker object should refer to the same object in memory, when it receives call from multiple threads
- So that it will be considered as all the threads are try to work on the same object(resource)
- Now let's add the lock using a string object with public access modifier
class JointAccount
{
int Balance = 10000;
public string locker = "";
public void WithDraw(int Amount)
{
lock (locker)
{
if (Amount <= Balance)
{
Console.WriteLine("Balance before withdraw: {0}", Balance);
Balance = Balance - Amount;
Console.WriteLine("Balance after withdraw: {0}", Balance);
}
else
{
Console.WriteLine("Cannot withdraw, as amount exceeds balance");
}
}
}
}
Balance before withdraw: 10000
Balance after withdraw: 0
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
{
int Balance = 10000;
public string locker = "";
public void WithDraw(int Amount)
{
lock (locker)
{
if (Amount <= Balance)
{
Console.WriteLine("Balance before withdraw: {0}", Balance);
Balance = Balance - Amount;
Console.WriteLine("Balance after withdraw: {0}", Balance);
}
else
{
Console.WriteLine("Cannot withdraw, as amount exceeds balance");
}
}
}
}
Balance before withdraw: 10000
Balance after withdraw: 0
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
- Now I have added the lock using a string object with access modifier public and it Works Fine. What is the issue?
- let's modify the code, to change the locker reference before each thread request and see what happens.
class Program
{
static void Main(string[] args)
{
JointAccount jointAccount = new JointAccount(); //Joint Account of 10 members
for (int i = 0; i < 10; i++)
{
jointAccount.locker = i.ToString();
new Thread(() => jointAccount.WithDraw(10000)).Start();
}
Console.ReadLine();
}
}
Balance before withdraw: 10000
Balance before withdraw: 10000
Balance after withdraw: -10000
Balance after withdraw: 0
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
{
static void Main(string[] args)
{
JointAccount jointAccount = new JointAccount(); //Joint Account of 10 members
for (int i = 0; i < 10; i++)
{
jointAccount.locker = i.ToString();
new Thread(() => jointAccount.WithDraw(10000)).Start();
}
Console.ReadLine();
}
}
Balance before withdraw: 10000
Balance before withdraw: 10000
Balance after withdraw: -10000
Balance after withdraw: 0
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
- After the changee, it may produce result like this, please read the Note, In a multi-threaded environment you cannot expect the same result all the times like single threaded
- There are chances, that the public member reference may change, that is why it is advisable to use private member.
- Now lets come to the point When to use lock(this)?
- In the prior example all the threads are working on a single instance of class. That is the Balance is commonly shared by all the threads. So here we can use lock(this) instead of having a separate locking object.
class JointAccount
{
int Balance = 10000;
public void WithDraw(int Amount)
{
lock (this)
{
if (Amount <= Balance)
{
Console.WriteLine("Balance before withdraw: {0}", Balance);
Balance = Balance - Amount;
Console.WriteLine("Balance after withdraw: {0}", Balance);
}
else
{
Console.WriteLine("Cannot withdraw, as amount exceeds balance");
}
}
}
public void displayBalance()
{
if(Balance<0)
Console.WriteLine("Final Balance {0}", Balance);
}
}
class Program
{
static void Main(string[] args)
{
JointAccount jointAccount = new JointAccount(); //Joint Account of 10 members
for (int i = 0; i < 10; i++)
{
new Thread(() => jointAccount.WithDraw(10000)).Start();
}
Console.ReadLine();
}
}
Balance before withdraw: 10000
Balance after withdraw: 0
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
{
int Balance = 10000;
public void WithDraw(int Amount)
{
lock (this)
{
if (Amount <= Balance)
{
Console.WriteLine("Balance before withdraw: {0}", Balance);
Balance = Balance - Amount;
Console.WriteLine("Balance after withdraw: {0}", Balance);
}
else
{
Console.WriteLine("Cannot withdraw, as amount exceeds balance");
}
}
}
public void displayBalance()
{
if(Balance<0)
Console.WriteLine("Final Balance {0}", Balance);
}
}
class Program
{
static void Main(string[] args)
{
JointAccount jointAccount = new JointAccount(); //Joint Account of 10 members
for (int i = 0; i < 10; i++)
{
new Thread(() => jointAccount.WithDraw(10000)).Start();
}
Console.ReadLine();
}
}
Balance before withdraw: 10000
Balance after withdraw: 0
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
Cannot withdraw, as amount exceeds balance
- When not to use lock(this) ?
- Refer the example below
class ThreadUnsafe {
static int _val1 = 1, _val2 = 1;
public void Go() {
if (_val2 != 0)
Console.WriteLine(_val1 / _val2);
_val2 = 0;
}
}
class Program
{
static void Main(string[] args)
{
Thread[] threads = new Thread[20];
for (int i = 0; i < 20; i++)
{
threads[i] = new Thread(() => new ThreadUnsafe().Go());
}
for (int i = 0; i < 20; i++)
{
threads[i].Start();
}
Console.ReadLine();
}
}
Answer is 1
Answer is 1
static int _val1 = 1, _val2 = 1;
public void Go() {
if (_val2 != 0)
Console.WriteLine(_val1 / _val2);
_val2 = 0;
}
}
class Program
{
static void Main(string[] args)
{
Thread[] threads = new Thread[20];
for (int i = 0; i < 20; i++)
{
threads[i] = new Thread(() => new ThreadUnsafe().Go());
}
for (int i = 0; i < 20; i++)
{
threads[i].Start();
}
Console.ReadLine();
}
}
Answer is 1
Answer is 1
- As you can see in the above example new instance of the class ThreadUnsafe is created with to call the Go method to update the static variable _val2.
- But more than one thread updated the value of _val2. Even chances are there to get
DivideByZeroException because one thread updated _val2 to 0 and other thread performing _val1 / _val2. - So to make it thread safe, if updated this code with lock(this)
class ThreadUnsafe {
static int _val1 = 1, _val2 = 1;
public void Go() {
lock (this)
{
if (_val2 != 0)
Console.WriteLine("Answer is {0}", _val1 / _val2);
_val2 = 0;
}
}
}
Answer is 1
Answer is 1
Answer is 1
static int _val1 = 1, _val2 = 1;
public void Go() {
lock (this)
{
if (_val2 != 0)
Console.WriteLine("Answer is {0}", _val1 / _val2);
_val2 = 0;
}
}
}
Answer is 1
Answer is 1
Answer is 1
- But still the code produces same kind of result
- Why this happens?
- Ok, lets update the code with instance level locker instead of lock(this) and see what happens
class ThreadUnsafe {
static int _val1 = 1, _val2 = 1;
object locker = new object();
public void Go() {
lock (locker)
{
if (_val2 != 0)
Console.WriteLine("Answer is {0}", _val1 / _val2);
_val2 = 0;
}
}
}
Answer is 1
Answer is 1
Answer is 1
static int _val1 = 1, _val2 = 1;
object locker = new object();
public void Go() {
lock (locker)
{
if (_val2 != 0)
Console.WriteLine("Answer is {0}", _val1 / _val2);
_val2 = 0;
}
}
}
Answer is 1
Answer is 1
Answer is 1
- From this we can understand one thing lock(this) and instance level locker is the same because of both work at the instance level, so each thread have its own object of locker/it is own object.
- So here it should not be be instance level locker, it should be a class level locker
- Now it works
class ThreadUnsafe {
static int _val1 = 1, _val2 = 1;
static object locker = new object();
public void Go() {
lock (locker)
{
if (_val2 != 0)
Console.WriteLine("Answer is {0}", _val1 / _val2);
_val2 = 0;
}
}
}
Answer is 1
static int _val1 = 1, _val2 = 1;
static object locker = new object();
public void Go() {
lock (locker)
{
if (_val2 != 0)
Console.WriteLine("Answer is {0}", _val1 / _val2);
_val2 = 0;
}
}
}
Answer is 1
Asynchronous Programming - System.Threading.Thread - Part 2
- Thread
- The Thread class was, originally, a 1:1 mapping to an operating system thread.
- It is typically used for long-running or specialized work such as monitoring a device or executing code with a low priority.
- Using the Thread class leaves us with a lot of control over the thread.
- Thread Class
- The Thread class is sealed
- Creating and Starting a Thread
Asynchronous Programming - Intro - Part 1
- C# supports parallel execution of code through multi-threading
- Multi-tasking Fundamentals
- There are two distinct types of multi-tasking:
- Process Based
- Thread Based
- Process Based
- A Process is a unit of Isolation on a single machine. Processes are fully isolated from each other;
- Multiple Processes do have to share access to the processing cores, but do not share virtual memory address space and can run in different security contexts.
- This is the model that web servers have used in the past to process multiple requests.
- Thread Based
- Threads are independently schedulable sets of instructions with a package of nonshared resources.
- A thread is bounded within a process.
- A thread cannot migrate from one process to another.
- All threads within a process share process-wide resources such as heap memory and operating system resources such as file handles and sockets.
- The queue-based approach is also applicable to multiple threads, as it is in fact a general purpose asynchronous pattern.
- Due to the heavy sharing of resources, using multiple threads benefits a lot from this approach.
- Resource sharing also introduces less complexity in coordinating multiple worker threads and handling thread failure.
- Why Multiple-Theads preferred over Multiple-Processes
- Windows processes are relatively heavyweight constructs when compared with threads.
- This is due to the loading of the Win32 runtime libraries and the associated registry reads (along with a number of cross-process calls to system components for housekeeping).
- Therefore, by design on Windows, we tend to prefer using multiple threads to create asynchronous processing rather than multiple processes.
- Thread Scheduling
- In Windows, the operating system component responsible for mapping thread execution on to cores is called the Thread Scheduler.
- As we shall see, sometimes threads are waiting for some event to occur before they can perform any work (in .NET this state is known as SleepWaitJoin).
- Any thread not in the SleepWaitJoin state should be allocated some time on a processing core and, all things being equal, the thread scheduler will round-robin processor time among all of the threads currently running across all of the processes.
- Each thread is allotted a time slice and, as long as the thread doesn’t enter the SleepWaitJoin state, it will run until the end of its time slice.
- Things, however, are not often equal.
- Different processes can run with different priorities (there are six priorities ranging from idle to real time).
- Within a process a thread also has a priority; there are seven ranging from idle to time critical.
- The resulting priority a thread runs with is a combination of these two priorities, and this effective priority is critical to thread scheduling.
- if a higher-priority thread wants to run, then a lower-priority thread is ejected from the processor (preempted) and replaced with the higher-priority thread.
- Threads of equal priority are, again, scheduled on a round-robin basis, each being allotted a time slice.
- You may be thinking that lower-priority threads could be starved of processor time. However, in certain conditions, the priority of a thread will be boosted temporarily to try to ensure that it gets a chance to run on the processor. Priority boosting can happen for a number of reasons (e.g., user input). Once a boosted thread has had processor time, its priority gets degraded until it reaches its normal value.
- Threads and Resources
- Although two threads share some resources within a process, they also have resources that are specific to themselves.
- Thread-Specific Resources
- The Stack
- Each thread gets its own stack. This means that local variables and parameters in methods, which are stored on the stack, are never shared between threads. The default stack size is 1MB, so a thread consumes a considerable amount of resource in just its allocated stack.
- Thread Local Storage
- All threads of a process share its virtual address space.
- The local variables of a function are unique to each thread that runs the function. However, the static and global variables are shared by all threads in the process.
- With thread local storage (TLS), you can provide unique data for each thread that the process can access using a global index.
- This value is specific to the thread and cannot be accessed by other threads.
- TLS slots are limited in number, The constant TLS_MINIMUM_AVAILABLE defines the minimum number of TLS indexes available in each process. which at the time of writing is guaranteed to be at least 64 per process but may be as high as 1,088.
- Registers
- A thread has its own copy of the register values.
- When a thread is scheduled on a processing core, its copy of the register value is restored on to the core’s registers. This allows the thread to continue processing at the point when it was preempted (its instruction pointer is restored) with the register state identical to when it was last running.
LINQ in C# - Non-Deferred Operator Examples - Aggregating
Count
Count - Type 1 Example 1
string[] names = {"Arun", "Raja", "Ramkumar", "RaviRajan" };
int Count = names.Count(); // 4
int Count = names.Count(); // 4
Count - Type 2 with Condition Example 1
string[] names = {"Arun", "Raja", "Ramkumar", "RaviRajan" };
int Count = names.Count(x=> x.Length==4); // 2
int Count = names.Count(x=> x.Length==4); // 2
LongCount
The LongCount operator returns the number of elements in the input sequence as a long.string[] names = {"Arun", "Raja", "Ramkumar", "RaviRajan" };
long Count = names.LongCount(x=> x.Length==4); // 2
long Count = names.LongCount(x=> x.Length==4); // 2
Sum
The Sum operator cannot have conditionsnt[] names = {1, 2, 3 , 4};
int sum= names.Sum()// 10
int sum= names.Sum()// 10
Min/Max
The Min/Max operator cannot have conditionsMin/Max - Type 1 - Example
int[] numbers = {1, 2, 3 , 4};
string[] names = { "Person A","Person B", "Person C", "Person D" };
string minString = names.Min(); // Person A
Console.WriteLine(minString);
int minInt = numbers.Min(); // 1
Console.WriteLine(minInt);
string[] names = { "Person A","Person B", "Person C", "Person D" };
string minString = names.Min(); // Person A
Console.WriteLine(minString);
int minInt = numbers.Min(); // 1
Console.WriteLine(minInt);
Min/Max - Type 2 with Property Selector- Example
List<Customer> Customers = new List<Customer> {
new Customer(){ CustomerID=1, Name="XYZ Private Limited", CustomerType="WholeSale" },
new Customer(){ CustomerID=2, Name="ABC Private Limited", CustomerType="WholeSale" },
new Customer(){ CustomerID=2, Name="MS Infotech", CustomerType="Retail" },
new Customer(){ CustomerID=4, Name="Oracle" , CustomerType="WholeSale" },
new Customer(){ CustomerID=5, Name="IBM Hardware Limited", CustomerType="Retail" },
};
string MinCustomerName = Customers.Min(x=> x.Name);
Console.WriteLine(MinCustomerName);
//Output
ABC Private Limited
new Customer(){ CustomerID=1, Name="XYZ Private Limited", CustomerType="WholeSale" },
new Customer(){ CustomerID=2, Name="ABC Private Limited", CustomerType="WholeSale" },
new Customer(){ CustomerID=2, Name="MS Infotech", CustomerType="Retail" },
new Customer(){ CustomerID=4, Name="Oracle" , CustomerType="WholeSale" },
new Customer(){ CustomerID=5, Name="IBM Hardware Limited", CustomerType="Retail" },
};
string MinCustomerName = Customers.Min(x=> x.Name);
Console.WriteLine(MinCustomerName);
//Output
ABC Private Limited
Average
The Average operator returns the average of numeric values contained in the elements of the input sequence.Average - Type 1 - Example 1
Average returns double return typeint[] numbers = {1, 2, 3 , 4};
double Avg = numbers.Average();
Console.WriteLine(Avg); // 2.5
double Avg = numbers.Average();
Console.WriteLine(Avg); // 2.5
Average - Type 2 with Property Selector- Example
class Order
{
public int OrderID { get; set; }
public int CustomerID { get; set; }
public DateTime OrderDate { get; set; }
public decimal Total { get; set; }
}
List<Order> Orders = new List<Order> {
new Order(){ OrderID=1, OrderDate=new DateTime(2020,10,1), CustomerID=1, Total=1025.24M },
new Order(){ OrderID=2, OrderDate=new DateTime(2020,4,1), CustomerID=1, Total=1000.24M },
new Order(){ OrderID=3, OrderDate=new DateTime(2020,4,1), CustomerID=2, Total=1140.24M },
new Order(){ OrderID=4, OrderDate=new DateTime(2020,2,1), CustomerID=2, Total=1160.24M },
new Order(){ OrderID=5, OrderDate=new DateTime(2020,1,1), CustomerID=3, Total=1567.24M },
};
decimal OrderAverage = Orders.Average(x=> x.Total);
Console.WriteLine(OrderAverage);
{
public int OrderID { get; set; }
public int CustomerID { get; set; }
public DateTime OrderDate { get; set; }
public decimal Total { get; set; }
}
List<Order> Orders = new List<Order> {
new Order(){ OrderID=1, OrderDate=new DateTime(2020,10,1), CustomerID=1, Total=1025.24M },
new Order(){ OrderID=2, OrderDate=new DateTime(2020,4,1), CustomerID=1, Total=1000.24M },
new Order(){ OrderID=3, OrderDate=new DateTime(2020,4,1), CustomerID=2, Total=1140.24M },
new Order(){ OrderID=4, OrderDate=new DateTime(2020,2,1), CustomerID=2, Total=1160.24M },
new Order(){ OrderID=5, OrderDate=new DateTime(2020,1,1), CustomerID=3, Total=1567.24M },
};
decimal OrderAverage = Orders.Average(x=> x.Total);
Console.WriteLine(OrderAverage);
Aggregate
The easiest-to-understand definition of Aggregate is that it performs an operation on each element of the list taking into account the operations that have gone before.Aggregate - Type 1 Example 1 Summing numbers
int[] numbers = {1, 2, 3 , 4};
var sum = numbers.Aggregate((a, b) => a + b); //output: 10 (1 + 2 + 3 + 4)
Console.WriteLine(sum);
This adds 1 and 2 to make 3. Then adds 3 (result of previous) and 3 (next element in sequence) to make 6. Then adds 6 and 4 to make 10. var sum = numbers.Aggregate((a, b) => a + b); //output: 10 (1 + 2 + 3 + 4)
Console.WriteLine(sum);
Aggregate - Type 1 Example 2 create a csv from an array of strings
var chars = new[] { "a", "b", "c", "d" };
var csv = chars.Aggregate((a, b) => a + ',' + b);
Console.WriteLine(csv); // Output a,b,c,d
var csv = chars.Aggregate((a, b) => a + ',' + b);
Console.WriteLine(csv); // Output a,b,c,d
Aggregate - Type 2 Example 1 Summing numbers with a seed
int[] numbers = {1, 2, 3 , 4};
var sum = numbers.Aggregate(10, (a, b) => a + b); //output: 20 = (10 + 1 + 2 + 3 + 4)
Console.WriteLine(sum);
var sum = numbers.Aggregate(10, (a, b) => a + b); //output: 20 = (10 + 1 + 2 + 3 + 4)
Console.WriteLine(sum);
Aggregate - Type 2 Example 2 with a seed
var chars = new[] { "a", "b", "c", "d" };
var csv = chars.Aggregate(new StringBuilder(), (a, b) => {
if (a.Length > 0)
a.Append(",");
a.Append(b);
return a;
});
Console.WriteLine(csv);
//Output
a,b,c,d
var csv = chars.Aggregate(new StringBuilder(), (a, b) => {
if (a.Length > 0)
a.Append(",");
a.Append(b);
return a;
});
Console.WriteLine(csv);
//Output
a,b,c,d
Subscribe to:
Posts (Atom)
Framework Fundamentals - String - Comparing Strings
In comparing two values, the .NET Framework differentiates the concepts of equality comparison and order comparison . Equality compariso...
-
The grouping operators assist with grouping elements of a sequence together by a common key. GroupBy operator return a sequence of IGrouping...
-
ToArray and ToList The ToArray operator creates an array of type T from an input sequence of type T. List<string> StudentNames = new L...
-
The partitioning operators allow you to return an output sequence that is a subset of an input sequence. Take - Example -1 class Course ...