LINQ / C#

LINQ Basics

Before we start learning LINQ language, we must learn some basic knowledge of most used concepts in LINQ.

Sequence

A sequence is any collection object that implements the IEnumerable<> interface. LINQ queries only works with the sequences.

static void Main(string[] args)
{
    List<string> countries = new List<string>();
    countries.Add("India");
    countries.Add("USA");
    countries.Add("UK");
    foreach(var country in countries)
    {
        Console.WriteLine(country);
    }
}

In the above example, List<string> has implement the IEnumerable interface and countries is the sequence.

Element

An element is an individual item in the Sequence. For example if sequence is composed with two objects, then this sequence has two elements.

As shown in the above example, each country string in the above countries variable is an element.

Query Operators

Query operators are functions that transforms a sequence. Sequence is passed to query operator as an input parameter. As a result, query operator change the sequence element and returns a result.

For example, there is a “Where” query operator in LINQ which takes an input sequence and a predicate to filter the elements. As an output it returns only those elements in a sequence that have matching filter condition.

Extension Methods

All LINQ language is based on extension methods. Extension methods are static methods of a static class that can be invoked just like an instance method. These methods are useful when we have to add new behaviors to an existing class without modifying the class.

class Program
{
    static void Main(string[] args)
    {
        string name = "Kapil";

        name = name.AddComma();

        Console.WriteLine(name); //Print Kapil,
    }
}

static class StringExtensionMethods
{
    public static string AddComma(this string input)
    {
        return input + ",";
    }
}

We cannot introduce new methods in the string class as we can not change the source code of string class. So we create a new extension method based on string class.

First we declare a static class StringExtensionMethods and declare a method AddComma. This first parameter of the method must be start with this and type name on which we want to add new method.

In the above example, string name variable has now a new method name AddComma which add “,” in the end of string.

You can learn more about extension methods from here.

Anonymous Types

Anonymous types are temporary classes that are useful for storing intermediate results. These types has not all the features of regular types. Below are the restrictions on Anonymous Types.

  • Anonymous types can only contain public fields.
  • Anonymous types fields must be initialized.
  • Anonymous types can not specify any methods.
  • Anonymous types cannot implement interface or abstract class.
var var1 = new { FirstName = "Kapil", LastName = "Malhotra" };

Console.WriteLine(var1.FirstName + " " + var1.LastName); //Print Kapil Malhotra

In the above example we have created a anonymous type which has two public fields FirstName and LastName. We have not specified any class name after the new keyword. This new anonymous type has only two properties and no methods.

Delegates

Delegates allows us to store a reference of functions that can be executed at appropriate time. In the delegate we can only store those functions that have matching parameters and return types.

For example, if we declare a delegate that accept an int parameter and returns string result. Then in that delegate we can only assign a reference to those functions that take int as a single parameter and returns string result.

class Program
{
    delegate int CalcSum(int a, int b);

    static void Main(string[] args)
    {
        CalcSum sumFunctions = Sum;
        int result = Sum(4,5);
        Console.WriteLine(result); //Print 9;

        sumFunctions = WrongSumFunction; //Gives Error at compile time
    }

    static int Sum(int a, int b)
    {
        return a + b;
    }

    static int WrongSumFunction(int a)
    {
        return a + a;
    }
}

In the above example, we declare a simple delegate CalcSum which takes two int parameters and return int result.

We declare two functions Sum and WrongSumFunction. Only Sum function has two int parameters and returns in result. We can easily assign this method to CalcSum instance. The second function WrongSumFunction takes only one parameter and returns int results. Because WrongSumFunction has only one int parameter we can not assign that method to CalcSum delegate instance as shown in the above example and it gives error at compile time.

Anonymous Methods

Anonymous Methods is an inline method that has only body without name. We can only define parameters and return types of the methods.

Below is an example of Anonymous Method.

class Program
{
    delegate int MyDelegate(int x, int y);
    static void Main(string[] args)
    {
        MyDelegate multiplyMethod = delegate(int x, int y)
        {
            return x * y;
        };

        int result = multiplyMethod(2, 4);
        Console.WriteLine(result); //Print 8
    }
}

In the above example, we create a delegate and assign an unnamed inline method to it. Method must have the same number of parameters and same type as matching with the delegate type.

Action<T> delegate

Action delegate is a predefined delegate by .NET framework which takes only parameters and does not return any value. We can specify maximum 16 parameters in the Action delegate.

Action<int, int> Multipler = delegate(int x, int y)
{
    Console.WriteLine(x * y);
};
Multipler(4, 5);    // Print 20

Predicate<T> delegate

Predicate delegate is a delegate which takes only single parameter and returns bool value.

static void Main(string[] args)
{
    Predicate<string> hasValueK = delegate(string par)
    {
        return par.Contains("K");
    };
    bool result1 = hasValueK("Kapil");
    bool result2 = hasValueK("Malhotra");

    Console.WriteLine(result1); // Print true
    Console.WriteLine(result2); // Print false
}

In the above example, we specify a predicate delegate hasValueK which takes only one string parameter and return boolean result.

Func<> delegate

Func delegate is also a generic delegate provided by .NET framework. In the Func delegate we can specify 16 parameters and a return type.

static void Main(string[] args)
{
    Func<int,int, int> multiplier = delegate(int a, int b)
    {
        return a * b;
    };
    int result = multiplier(2, 9);

    Console.WriteLine(result); // Print 18
}

In the above example, we specify a Func<int, int, int> delegate. The last int is a result type and first two int are parameters types.

Lambda Expressions

Lambda Expressions is just a shortcut way to writing anonymous methods. => is a lambda operator. On the left of lambda operator we specify the input parameters and on the right side we write the body of the method.

delegate int MultiplierDelegate(int a, int b);
static void Main(string[] args)
{

    MultiplierDelegate multiplier = (a, b) => a * b;
    int result = multiplier(2, 9);

    Console.WriteLine(result); // Print 18
}