Widely asked in interviews: Delegate, Linq, Lambda Expn Create Extension Method & Expression Tree
In technical interviews most of the times, TRICKY questions are fired on topics; Delegate, Anonymous Method, Anonymous Type, LINQ, Extension Method, Lambda Exp, Expression Tree.....
Though most of us know these concepts but get messed-up and unable to explain in interviews as required.....
This article covers these topics with Code-Snippets that others can refer to understand the topics.
We will Start with 'Delegate' and will complete the article with 'Writing your own Custom Extension methods'
## Delegate:
Delegate in C# is a way to pass function as parameter along with parameters without let the developer know where the actual function resides.
Developer just need to call the delegate object with the function's parameters.
Delegate has following features:
1. Functions can be passed in its constructor
2. Parameters can be pass to those functions
3. Can be used as callback methods (this feature is out of scope of this Article, hence I have not included in code-snippets)
4. Does not require name
Sample Code
// Delegate with named function
public class Approach1
{
// 1. Declare function
public string MyFunction(string name, string department, decimal experience)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("Employee Name: " + name);
sb.AppendLine("Department Name: " + department);
sb.AppendLine("Experience: " + experience.ToString() + " years");
return sb.ToString();
}
public delegate string MyDelegate(string name, string department, decimal experience);
// Test method
public void Test()
{
// 3. Instentiate delegate
MyDelegate myDelegate = new MyDelegate(MyFunction);
String n = "Some Name";
String d = "Some Department";
decimal e = 5.2M;
// 4. Call the function
string Detail = myDelegate(n, d, e);
Console.WriteLine("Approach - 1 (delegate with named function)");
Console.WriteLine("Employee Detail ...");
Console.WriteLine(Detail);
Console.ReadKey();
}
}
## Anonymous Method:
An Anonymous Method is a method body scopped with { ..... } which can be defined with a delegate().
Anonymous method has following features:
1. Can be define inline
2. Does not require name
3. Does not require parameter
4. Does not require to return a value
5. Eliminates declaration of delegate
Sample Code
// Delegate WITH anonymous method and parameter passing
public class Approach2
{
// 1. Declare delegate
public delegate string MyDelegate(string name, string department, decimal experience);
public void Test()
{
// 2. Instentiate delegate with anonymous method (and parameters)
MyDelegate myDelegate = delegate(string name, string department, decimal experience)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("Employee Name: " + name);
sb.AppendLine("Department Name: " + department);
sb.AppendLine("Experience: " + experience.ToString() + " years");
return sb.ToString();
};
String n = "Some Name";
String d = "Some Department";
decimal e = 5.2M;
// 3. Call the function
string Detail = myDelegate(n, d, e);
Console.WriteLine("Approach - 2 (delegate WITH anonymous method and parameter passing)");
Console.WriteLine("Employee Detail ...");
Console.WriteLine(Detail);
Console.ReadKey();
}
}
Another Example:
// Delegate WITH anonymous method ( WITHOUT parameter passing )
public class Approach3
{
// 1. Declare delegate
public delegate string MyDelegate();
public void Test()
{
String n = "Some Name";
String d = "Some Department";
decimal e = 5.2M;
// 2. Instentiate delegate with anonymous method (without parameters)
MyDelegate myDelegate = delegate()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("Employee Name: " + n);
sb.AppendLine("Department Name: " + d);
sb.AppendLine("Experience: " + e.ToString() + " years");
return sb.ToString();
};
// 3. Call the function
string Detail = myDelegate();
Console.WriteLine("Approach - 3 (delegate WITH anonymous method and WITHOUT parameter passing)");
Console.WriteLine("Employee Detail ...");
Console.WriteLine(Detail);
Console.ReadKey();
}
}
## Anonymous Type:
An Anonymous Type is a way that lets the developer bundle a set of properties (of various types) in one object whose type is unknown at design-time/run-time.
This approach gives us a flexibility to create a complex object, collection or a combonation of both.
Rules for Anonymous Types:
1. Internally these types are derived from System.Object class
2. To access properties of object of type 'Anonymous Types' which is boxed as Object type, we need to Cast back with dynamic as follow:
Right Approach:
var v = new { e = 3, n = "Sharad" };
Object o = v; // boxed
int experience = ((dynamic)o).e;
string name = ((dynamic)o).n;
Right:
var v = new { e = 3, n = "Sharad" };
Object o = v as Object;
dynamic d = o;
int experience = d.e;
string name = d.n;
Wrong Approach:
Object o = new { e = 3, n = "Sharad" };
var v = o;
int experience = v.e;
string name = v.n;
1. In the same way if we want to pass Anonymous Type objects to some method as parameter, we need to receive them as 'dynamic' or 'object' as follows:
public void Test()
{
.....
var v = new { e = 3, n = "Sharad" };
TestFunction(v);
.....
}
public void TestFunction(dynamic d)
{
.....
int experience = d.e;
string name = d.n;
.....
}
2. Properties of such objects remain read-only
3. These Types cannot be used as Property types or Method's return types
## Linq:
Linq stands for "Language Integrated Query". DotNet Linq facilitates an approach for general-purpose query building and executing with the standard SQL flavore.
Ling provides various features to the developers like:
1. Queries can be built on metadata available to the application
2. Verify design/compile time syntax
3. Intellisense with the objects and properties
4. Process information from datasources like; tables, objects, xml etc
5. Standard sql features are available like; filtering, projection, joins, grouping, ordering etc
6. Can be applied to all the objects that implement IEnumerable
In it's most simplest form, a Linq query can be written as follows:
IEnumerable<string> invalidNames = from n in EmployeeNameCollection where n.Length<2 order by n select n
Syntax:
IEnumerable<anyType> list = from <alias> in <IEnumerable collection> where <expression based on alias> order by <alias.properties, ...> select <alias>....;
Sample Code:
// Linq with Anonymous Type
public class Approach4
{
public class Employee
{
public Employee(String Name, String Department, Decimal Experience)
{
this.Name = Name;
this.Department = Department;
this.Experience = Experience;
}
public string Name { get; set; }
public string Department { get; set; }
public decimal Experience { get; set; }
}
public void Test()
{
List<Employee> Employees = new List<Employee>(){new Employee("Ram","Sales",5.5M),new Employee("Ajay","HR",3.5M),new Employee("Joy","IT",4.5M)};
// 1. Linq Query with Extension Method (result will be of Anonymous Type)
var detailInfo = (from n in Employees
where n.Experience > 4
select new {detail = String.Format("Employee Name:{0}\nDepartment Name:{1}\nExperience:{2} years\n",n.Name,n.Department,n.Experience.ToString())}
).First();
// 2. Accessing property (detail) from the object (detailInfo) of Anonymous Type
string detail = detailInfo.detail;
Console.WriteLine("Approach - 4 (Linq, Anonymous Type)");
Console.WriteLine("Employee Detail ...");
Console.WriteLine(detail);
Console.ReadKey();
}
}
## Built-in Extension methods:
Query given in above code-snippet can also be written with built-in method expressions as follows:
IEnumerable<string> invalidNames = EmployeeNameCollection.Where( n => n.Length<2 ).OrderBy( n => n).Select( n => n);
Here the methods Where(.=>.), OrderBy(.=>.), Select(.=>.) are Built-in Extension Methods of Linq.
We can also create our own Custom Extension Methods for Custom Types (like List<Employee>)... (Extension Methods are explained later in this article)
Some of the Built-in Extension Methods are:
1. Distinct(): To return distinct set of records from a collection:
Sample Code:
public class MyClass
{
public int c { get; set; }
public string n { get; set; }
}
public class MyEquality : IEqualityComparer<MyClass>
{
private static int i;
static MyEquality()
{
i = 1;
}
public bool Equals(MyClass o1, MyClass o2)
{
return o1.c.Equals(o2.c) && o1.n.Equals(o2.n);
}
public int GetHashCode(MyClass o)
{
return o.c.GetHashCode();
}
}
static void Main(string[] args)
{
List<MyClass> list = new List<MyClass>() {
new MyClass{ c = 1, n = "Raj" },
new MyClass{ c = 4, n = "Ajay" },
new MyClass{ c = 2, n = "Pol" },
new MyClass{ c = 2, n = "Pol" },
new MyClass{ c = 20, n = "Pol" },
new MyClass{ c = 20, n = "Nik" },
new MyClass{ c = 4, n = "Sam" },
new MyClass{ c = 3, n = "Viki" }
};
IEqualityComparer<MyClass> me = new MyEquality();
var v = list.Distinct(me); // o/p {1,"Raj"}, {4, "Ajay"}, {2, "Pol"}, {20, "Pol"}, {20, "Nik"}, {4, "Sam"}, {3, "Viki"}
}
2. Where(): To apply a where criteris on collection:
Sample Code:
List<int> list = new List<int>() { 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 9, 10 };
var v2 = list.Where(n => n % 2 == 0); // o/p 2,4,6,6,8,10
3. Select(): It projects on the current record/entity from comming sequence and allows us to process specific property of that record/entity. { The Select method actualy execute the Linq query }
Sample Code:
List<int> list = new List<int>() { 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 9, 10 };
var v3 = list.Select(n => (n % 2 == 0 ? "Even" : "Odd") + " Number"); // o/p Even, Odd, Odd, Even, Odd, Odd, Even, Even, Odd, Even, Odd, Even
4. OrderBy(), OrderByDescending(): To define the Order of returned records as Ascending, Descending.
Sample Code:
List<int> list = new List<int>() { 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 9, 10 };
var v4 = list.OrderBy(n => n); // o/p 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 9, 10
var v5 = list.OrderByDescending(n => n); // o/p 10, 9, 8, 7, 6, 6, 5, 5, 4, 3, 3, 2, 1
5. GroupBy(): To return a collection based on grouping on desired properties.
Sample Code:
// Example Due
6. Join(): To join collections and returns the resultant as a new collection:
Sample Code:
public class Employee
{
public int eCode { get; set; }
public string eName { get; set; }
public int dCode { get; set; }
}
public class Designation
{
public int dCode { get; set; }
public string dName { get; set; }
}
static void Main(string[] args)
{
List<Employee> employees = new List<Employee>() {
new Employee{ eCode = 1, eName = "Raj", dCode = 1 },
new Employee{ eCode = 4, eName = "Ajay", dCode = 3 },
new Employee{ eCode = 2, eName = "Pol", dCode = 2},
new Employee{ eCode = 20, eName = "Nik", dCode = 3},
new Employee{ eCode = 3, eName = "Viki", dCode = 3}
};
List<Designation> departments = new List<Designation>() {
new Designation{ dCode=1, dName="Architech" },
new Designation{ dCode=2, dName="Lead" },
new Designation{ dCode=3, dName="Developer" },
};
var employeeDetail = employees.Join( departments, e => e.dCode, d => d.dCode, (e,d) => new { e.eCode, e.eName, e.dCode, d.dName} );
}
7. Intersect(): To find intersection of collections and returns the resultant as a new collection:
Sample Code:
List<String> employees1 = new List<String>() { "Raj", "Ajay" , "Pol"};
List<String> employees2 = new List<String>() { "Pol", "Raj", "Nik" };
var v = employees1.Intersect(employees2); // o/p "Raj", "Pol"
8. Concat(): To Concatinate one collection to another and returns the resultant as a new collection:
Sample Code:
List<String> employees1 = new List<String>() { "Raj", "Ajay" , "Pol"};
List<String> employees2 = new List<String>() { "Pol", "Raj", "Nik" };
var v = employees1.Concat(employees2); // o/p "Raj", "Ajay" , "Pol","Pol", "Raj", "Nik"
9. Contains(): To verify whether a collection contains some member .. It returns boolean
Sample Code:
List<String> employees1 = new List<String>() { "Raj", "Ajay" , "Pol", "Nik" };
var v = employees1.Contains("Pol"); // o/p true
10. First(): To return First record of source collection:
Sample Code:
List<String> employees1 = new List<String>() { "Raj", "Ajay" , "Pol", "Nik" };
var v = employees1.First(); // o/p "Raj"
11. Count(), Sum(), Average(), Max(), Min(): To return results of various Aggregate functions.
Sample Code:
List<double> salaries = new List<double>() { 10000, 20000, 45000, 43000, 12345, 43234 };
var count = salaries.Count(); // o/p 6
var sum = salaries.Sum(); // o/p 173579.0
var average = salaries.Average(); // o/p 28929.833333333332
var max = salaries.Max(); // o/p 45000.0
var min = salaries.Min(); // o/p 10000.0
## Lambda Expression:
In above queries the Expression Arguments passed to the Extension methods are called "Lambda Expressions",
Which consist of 3 parts :
1. Left-part: Parameters for execulable part
2. Goes-to-operator: Symbol '=>'
3. Right-part: Executable part
And, the code fregment ' n.Length < 2 ' is similar to anonymous method.
Hence, a Lambda Expression can be defined as an Anonymous method/function that can be passed as a parameter to Extension Methods.
SampleCode:
// Lambda Expression
public class Approach5
{
public class Employee
{
public Employee(String Name, String Department, Decimal Experience)
{
this.Name = Name;
this.Department = Department;
this.Experience = Experience;
}
public string Name { get; set; }
public string Department { get; set; }
public decimal Experience { get; set; }
}
public void Test()
{
List<Employee> Employees = new List<Employee>() { new Employee("Harish", "Sales", 5.5M), new Employee("Aakash", "Excise", 3.5M), new Employee("Mahesh", "IT", 4.5M) };
// 1. Lambda Expression
string Detail = Employees.Where(n => n.Experience > 4).Select(n =>
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("Employee Name: " + n.Name);
sb.AppendLine("Department Name: " + n.Department);
sb.AppendLine("Experience: " + n.Experience.ToString() + " years");
return sb.ToString();
}).First();
Console.WriteLine("Approach - 5 (Lambda Expression)");
Console.WriteLine("Employee Detail ...");
Console.WriteLine(Detail);
Console.ReadKey();
}
}
## Expression Tree:
Expression tree feature allows developer to build custom LINQ queries at run-time.
These custom queries are useful when developer does not know in design-time, that what internals would be there in the lambda expressions.
Internally these trees works with queries that need IQueryable data so that the LINQ query engine can traverse the tree to convert it into a query language, compatible to desired datatype.
Steps to create and use Expression tree:
0. Consider complete syntax of Lambda Expression: parameter-part => execution-part
1. Create "parameter-part" as ParameterExpression type.
2. Create Expression tree statement as Expression< Func< ClassType, DataType > > type.
3. The tree is created with ParameterExpression and PropertyName.
4. 'Func< ClassType, DataType >' is Generic Delegate to which we pass 2 parameters; ClassType and the return DataType.
5. Call AsQueryable() method of collection which returns 'object' that implements IQueryable & IEnumerable interfaces.
6. Call any Extension method of this 'object' and pass Expression Tree statement to it to get desired result.
(Refer following Code snippet)
Consider, we have a collection of objects of Employee class type:
SampleCode:
public class Employee
{
public int Code { get; set; }
public string Name { get; set; }
public decimal AspNetExp { get; set; }
public decimal DatabaseExp { get; set; }
}
public class SomeLibrary
{
public static Expression<Func<Employee, decimal>> CreateExpression(string propertyName)
{
var parameterExpression = Expression.Parameter(typeof(Employee));
var expression = Expression.Lambda<Func<Employee, decimal>>(Expression.Property(parameterExpression, propertyName), parameterExpression);
return expression;
}
// Other helper methods
// ...
// ...
}
static void Main(string[] args)
{
List<Employee> employees = new List<Employee>() {
new Employee{ Code = 1, Name = "Raj", AspNetExp = 1.4M, DatabaseExp=1.2M },
new Employee{ Code = 4, Name = "Ajay", AspNetExp = 2.5M, DatabaseExp=2.8M },
new Employee{ Code = 2, Name = "Pol", AspNetExp = 5.1M, DatabaseExp=5.3M },
new Employee{ Code = 20, Name = "Nik", AspNetExp = 4.0M, DatabaseExp=5.1M },
new Employee{ Code = 3, Name = "Viki", AspNetExp = 3.6M, DatabaseExp=2.8M }
};
var v1 = employees.AsQueryable().Select<Employee, decimal>(Library.CreateExpression("AspNetExp"));
var v2 = employees.AsQueryable().Select<Employee, decimal>(Library.CreateExpression("DatabaseExp"));
}
Where:
1). We are passing "AspNetExp" & "DatabaseExp" to the CreaterBuilder helper method.
2). This helper method will be pushed into Expression.Lambda() Factory Method.
3). This factory method defines the internals for our LINQ query at run-time.
4). This gives flexibility to decide at run-time, that which property of all sequence should reflect as result.
## Writing your Own Custom Extension Methods:
Extension Methods give the developer flexibility to extens a Base type or Custom type without modifying/recompile them.
Once created these methods can be called just like normal methods available in that type.
Steps to create Extension Methods:>
1. Create static class (eg. MyExtensionMethodLibrary) in SAME namespace.
2. Create static method in this class which:
(i) Receives first parameter with 'this' keyword. The parameter type should be same as the SourceType for which we are creating Extension method.
(ii) Returns the desired type (optional).
3. Once created, the same method will be available with all objects of Same Type.
4. In our sample code following Extension Method works with 'Employee class' and 'return type as decimal'.
SampleCode:
// I have restructured "Expression Tree" POC to meet requirements for "Extension Methods"
namespace Approaches
{
public class Employee
{
public int Code { get; set; }
public string Name { get; set; }
public decimal AspNetExp { get; set; }
public decimal DatabaseExp { get; set; }
}
public class Library
{
public static Expression<Func<Employee, decimal>> CreateExpression(string propertyName)
{
var parameterExpression = Expression.Parameter(typeof(Employee));
var expression = Expression.Lambda<Func<Employee, decimal>>(Expression.Property(parameterExpression, propertyName), parameterExpression);
return expression;
}
}
public static class MyExtensionMethodLibrary
{
public static IQueryable FetchRecords(this List<Employee> sourceList, string propertyName)
{
IQueryable result = sourceList.AsQueryable().Select<Employee, decimal>(Library.CreateExpression(propertyName));
return result;
}
}
class Program
{
public void TestExtensionMethod()
{
List<Employee> employees = new List<Employee>() {
new Employee{ Code = 1, Name = "Raj", AspNetExp = 1.4M, DatabaseExp=1.2M },
new Employee{ Code = 4, Name = "Ajay", AspNetExp = 2.5M, DatabaseExp=2.8M },
new Employee{ Code = 2, Name = "Pol", AspNetExp = 5.1M, DatabaseExp=5.3M },
new Employee{ Code = 20, Name = "Nik", AspNetExp = 4.0M, DatabaseExp=5.1M },
new Employee{ Code = 3, Name = "Viki", AspNetExp = 3.6M, DatabaseExp=2.8M }
};
var v1 = employees.FetchRecords("AspNetExp");
var v2 = employees.FetchRecords(DatabaseNetExp");
}
}
I hope all the topics are coveded properly along with example.
Samples available in this resource are written as per my understanding. To report a technical issue or suggestion / query,
please do post it.
Thanks!
Hi,
Very helpful question.
Thanks for share