/// <summary>
        ///     Generic extension method yielding objects of specified type from table.
        /// </summary>
        /// <remarks>
        ///     Only primitives and enums are supported as property.
        ///     Currently supports only tables with header.
        /// </remarks>
        /// <typeparam name="T">Type to map to. Type should be a class and should have parameter-less constructor.</typeparam>
        /// <param name="table">Table object to fetch</param>
        /// <param name="configurationAction"></param>
        /// <returns>An enumerable of the generating type</returns>
        public static IEnumerable <T> AsEnumerable <T>(this ExcelTable table, Action <ExcelReadConfiguration <T> > configurationAction = null) where T : new()
        {
            ExcelReadConfiguration <T> configuration = ExcelReadConfiguration <T> .Instance;

            configurationAction?.Invoke(configuration);

            if (table.IsEmpty(configuration.HasHeaderRow))
            {
                yield break;
            }

            List <ExcelTableColumnDetails> mapping = PrepareMappings(table, configuration).Where(x => x.ColumnPosition >= 0).ToList();

            ExcelAddress bounds = table.GetDataBounds();

            // Parse table
            for (int row = bounds.Start.Row; row <= bounds.End.Row; row++)
            {
                var item = new T();

                foreach (ExcelTableColumnDetails map in mapping)
                {
                    var    exists = table.WorkSheet.Cells[row, map.ColumnPosition + table.Address.Start.Column];
                    object cell   = exists.Value;

                    PropertyInfo property = map.PropertyInfo;

                    try
                    {
                        TrySetProperty(item, property, cell);
                    }
                    catch (Exception ex)
                    {
                        var exceptionArgs = new ExcelExceptionArgs
                        {
                            ColumnName   = table.Columns[map.ColumnPosition].Name,
                            ExpectedType = property.PropertyType,
                            PropertyName = property.Name,
                            CellValue    = cell,
                            CellAddress  = new ExcelCellAddress(row, map.ColumnPosition + table.Address.Start.Column)
                        };

                        if (configuration.ThrowValidationExceptions && ex is ValidationException)
                        {
                            throw new ExcelValidationException(ex.Message, ex)
                                  .WithArguments(exceptionArgs);
                        }

                        if (configuration.ThrowCastingExceptions)
                        {
                            throw new ExcelException(string.Format(configuration.CastingExceptionMessage, exceptionArgs.ColumnName, exceptionArgs.CellAddress.Address, exceptionArgs.CellValue, exceptionArgs.ExpectedType.Name), ex)
                                  .WithArguments(exceptionArgs);
                        }
                    }
                }

                configuration.OnCaught?.Invoke(item, row);
                yield return(item);
            }
        }
        /// <summary>
        ///     Generic extension method yielding objects of specified type from table.
        /// </summary>
        /// <remarks>
        ///     Only primitives and enums are supported as property.
        ///     Currently supports only tables with header.
        /// </remarks>
        /// <typeparam name="T">Type to map to. Type should be a class and should have parameterless constructor.</typeparam>
        /// <param name="table">Table object to fetch</param>
        /// <param name="configurationAction"></param>
        /// <returns>An enumerable of the generating type</returns>
        public static IEnumerable <T> AsEnumerable <T>(this ExcelTable table, Action <ExcelReadConfiguration <T> > configurationAction = null) where T : class, new()
        {
            ExcelReadConfiguration <T> configuration = DefaultExcelReadConfiguration <T> .Instance;

            configurationAction?.Invoke(configuration);

            if (!table.IsEmpty(configuration.HasHeaderRow))
            {
                List <KeyValuePair <int, PropertyInfo> > mapping = PrepareMappings(table, configuration).ToList();

                ExcelAddress bounds = table.GetDataBounds();

                // Parse table
                for (int row = bounds.Start.Row; row <= bounds.End.Row; row++)
                {
                    var item = (T)Activator.CreateInstance(typeof(T));

                    foreach (KeyValuePair <int, PropertyInfo> map in mapping)
                    {
                        object cell = table.WorkSheet.Cells[row, map.Key + table.Address.Start.Column].Value;

                        PropertyInfo property = map.Value;

                        try
                        {
                            TrySetProperty(item, property, cell);
                        }
                        catch (Exception ex)
                        {
                            var exceptionArgs = new ExcelExceptionArgs
                            {
                                ColumnName   = table.Columns[map.Key].Name,
                                ExpectedType = property.PropertyType,
                                PropertyName = property.Name,
                                CellValue    = cell,
                                CellAddress  = new ExcelCellAddress(row, map.Key + table.Address.Start.Column)
                            };

                            if (configuration.ThrowValidationExceptions && ex is ValidationException)
                            {
                                throw new ExcelValidationException(ex.Message, ex)
                                      .WithArguments(exceptionArgs);
                            }

                            if (configuration.ThrowCastingExceptions)
                            {
                                throw new ExcelException(string.Format(configuration.CastingExceptionMessage, exceptionArgs.ColumnName, exceptionArgs.CellAddress.Address, exceptionArgs.CellValue, exceptionArgs.ExpectedType.Name), ex)
                                      .WithArguments(exceptionArgs);
                            }
                        }
                    }

                    configuration.OnCaught?.Invoke(item, row);
                    yield return(item);
                }
            }
        }