Recently I was asked a question, as the title mentions of this post.
And I was just left with the ?? (Question) because I didn’t digged in so far previously in LINQ world before.
So let me start,
IEnumerable: This interface is designed to expose only the Enumerator, which would support a simple iteration over a non- generic collection.
This interface only enforces one method to be implemented by the class, with signature as
public IEnumerator GetEnumerator()
An example illustrating the sample implementation of IEnumerable
using System.Collections; using System.Collections.Generic; namespace LinqInterfacesExamples.Model { public class ExampleIEnumerable : IEnumerable { private readonly string[] _internalList; private int _count; /// <summary> /// This is a constructor for the IEnumrable implementation class /// </summary> public ExampleIEnumerable() { _internalList = new string[100]; _count = 0; } /// <summary> /// This is the method to populate the items into the internal array of the class /// </summary> /// <param name="value">This is a string value for to be added to the internal class list</param> public void Add(string value) { _internalList[_count] = value; _count++; } /// <summary> /// This is the interface method which need to be implemented for the IEnumerable interface /// </summary> /// <returns>IEnumerator</returns> public IEnumerator GetEnumerator() { for (int i = 0; i < _count; i++) { yield return _internalList[i]; } } } }
In the above example I have explicitly added the method to append items to the collection via “Add” method.
Note: The class is using a simple string array to store the items being added to the collection but still we are able to enumerate upon the same via the help of implementing the IEnumerable interface.
Comments: This is quick, good and simple methodology to expose non enumerable features of collections in an object with the help of “yield” keyword.
The implementation of the same would allow the use of “foreach” loop construct.
IEnumerator: This interface give the implementer a next level of granularity for the object to implement the following methods as follows:
Method
MoveNext
Reset
Property
Current
This interface most commonly is used to implement and return the object via custom class implementing the “IEnumerable” via the “GetEnumerator”.
The main purpose to use this interface to custom implement is for the following “Supports a simple iteration over a nongeneric collection.” As provided by MSDN link.
An example illustrating the sample implementation of IEnumerator
using System.Collections; namespace LinqInterfacesExamples.Model { public class ExampleIEnumerator : IEnumerator { private object _current; private string[] _internalList; private int _count; private int _maxCount; public ExampleIEnumerator() { _internalList = new string[100]; } public void Add(string value) { _internalList[_count] = value; if (_count > _maxCount) _maxCount = _count; _count++; } public bool MoveNext() { if(++_count < _maxCount) { _current = _internalList[_count]; return true; } return false; } public void Reset() { _count = -1; _maxCount = -1; } public object Current { get { return _current; } } } }
Comments: Thus using this interface allows the user of the implementation object to custom move the records by using the while loop and have the counter reset on the user command.
The implementation of the same would NOT allow the use of foreach loop construct.
IQueryable/ IQueryProvider
Pheww… Now the big ones…
IQueryable is more preferred to query data from out-memory (like remote database, service) collections.
Vs…
IEnumerable is more preferred for in-memory collection of data.
Simplifying above the IEnumerable collections first loads in all the data to the memory then applies the filter operation by the nature of the interface, where as the IQueryable interface is designed to modify the data retrieval query while being fired thus bringing in lesser amounts of data to the in-process memory and optimal for paging and incremental loading of data.
But interstingly IQueryable extends from IEnumerable*.
IQueryable works in conjunction with IQueryProvider whose interfaces are defined with the following methods:
IQueryable:
Properties
public Expression Expression {get;} public Type ElementType {get;} public IQueryProvider Provider {get;}
* IEnumerable (as IQueryable extends from the same):
Method
IEnumerator IEnumerable.GetEnumerator()
IQueryProvider:
Method
IQueryable<S> IQueryProvider.CreateQuery<S>(System.Linq.Expressions.Expression expression) IQueryable IQueryProvider.CreateQuery(System.Linq.Expressions.Expression expression) TResult IQueryProvider.Execute<TResult>(System.Linq.Expressions.Expression expression) object IQueryProvider.Execute(System.Linq.Expressions.Expression expression)
The sample implementation of the same will be as follows:
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; namespace LinqInterfacesExamples.Model { public class ExampleIQueryable: IQueryable<Employee> , IQueryProvider { private IList<Employee> _employee = new List<Employee>(); private Expression _expression = null; #region Implementation of IEnumerable IEnumerator<Employee> IEnumerable<Employee>.GetEnumerator() { return (this as IQueryable).Provider.Execute<IEnumerator<Employee>>(_expression); } IEnumerator IEnumerable.GetEnumerator() { return (IEnumerator<Employee>) (this as IQueryable).GetEnumerator(); } #endregion #region Implementation of IQueryable public Expression Expression { get { return Expression.Constant(this); } } public Type ElementType { get { return typeof (Employee); } } public IQueryProvider Provider { get { return this; } } #endregion #region Custom Methods private void ProcessExpression(Expression expression) { if (expression.NodeType == ExpressionType.Equal) { ProcessEqualResult((BinaryExpression)expression); } if (expression.NodeType == ExpressionType.LessThan) { _employee = GetPersons(); var query = from p in _employee where p.Age < (int)GetValue((BinaryExpression)expression) select p; _employee = query.ToList<Employee>(); } if (expression is UnaryExpression) { UnaryExpression uExp = expression as UnaryExpression; ProcessExpression(uExp.Operand); } else if (expression is LambdaExpression) { ProcessExpression(((LambdaExpression)expression).Body); } else if (expression is ParameterExpression) { if (((ParameterExpression)expression).Type == typeof(Employee)) { _employee = GetPersons(); } } } private void ProcessEqualResult(BinaryExpression expression) { if (expression.Right.NodeType == ExpressionType.Constant) { string name = (String)((ConstantExpression)expression.Right).Value; ProceesItem(name); } } private void ProceesItem(string name) { IList<Employee> filtered = new List<Employee>(); foreach (Employee person in GetPersons()) { if (string.Compare(person.FirstName, name, true) == 0) { filtered.Add(person); } } _employee = filtered; } private object GetValue(BinaryExpression expression) { if (expression.Right.NodeType == ExpressionType.Constant) { return ((ConstantExpression)expression.Right).Value; } return null; } IList<Employee> GetPersons() { return new List<Employee> { new Employee { ID = 1, FirstName= "Mehfuz Hossain", Age=27}, new Employee { ID = 2, FirstName= "Json Born", Age=30}, new Employee { ID = 3, FirstName= "John Doe", Age=52} }; } #endregion #region Implementation of IQueryProvider IQueryable<S> IQueryProvider.CreateQuery<S>(System.Linq.Expressions.Expression expression) { if (typeof(S) != typeof(Employee)) throw new Exception("Only " + typeof(Employee).FullName + " objects are supported."); this._expression = expression; return (IQueryable<S>)this; } IQueryable IQueryProvider.CreateQuery(System.Linq.Expressions.Expression expression) { return (IQueryable<Employee>)(this as IQueryProvider).CreateQuery<Employee>(expression); } TResult IQueryProvider.Execute<TResult>(System.Linq.Expressions.Expression expression) { MethodCallExpression methodcall = _expression as MethodCallExpression; if (methodcall != null) foreach (var param in methodcall.Arguments) { ProcessExpression(param); } return (TResult)_employee.GetEnumerator(); } object IQueryProvider.Execute(System.Linq.Expressions.Expression expression) { return (this as IQueryProvider).Execute<IEnumerator<Employee>>(expression); } #endregion } }
In the above example the same class which implements the IQueryable implements the IQueryProvider which is then also returned as Provider property.
The custom methods section (region) in the above code is designed for the expression tree parsing which I will explain in my subsequent blog post. But to keep it simple, this is the place where the LINQ logic gets executed upon the collection to filter out the results and return.
IQueryProvider, this section implements the methods CreateQuery/Execute where the method “Create” initializes the expression property of the IQueryProvider and upon which the “Execute” method is fired when the expression is actually evaluated (thus being lazy-loaded).
Comment: The implementation of the same would NOT allow the use of foreach loop construct.
The use of IQueryable is mostly used in the areas of querying external databases, web service collection or areas where query translation would help generate performance and additional benefits. (ex. ADO.NET Entity Framework)
The above code may look complex (for IQueryable interface) but digging in through the example via code stepping would help you understand the same.
Thank you, and please comment your views on the same and any corrections required for the same.
Downloads:
Download