/// <summary>
    /// Parsing CSV file contetnt to object T
    /// </summary>
    /// <typeparam name="T">Generic object</typeparam>
    /// <param name="data">CSV file content</param>
    /// <param name="delimiter">Delimiter char, used in CSV file</param>
    /// <param name="stringSplitOptions">Specifies whether applicable Overload:System.String.Split method overloads include or omit empty substrings from the return value.</param>
    /// <returns>List with T objects parsed from CSV</returns>
    public static List <T> ParseCsvToObject <T>(string data, char delimiter = ',', StringSplitOptions stringSplitOptions = StringSplitOptions.None) where T : new()
    {
        List <T> listOfObjects = new List <T>();

        char[] separator = { delimiter };

        using (StringReader reader = new StringReader(data))
        {
            int    lineNo = 0;
            string line   = string.Empty;
            Dictionary <string, int> headerSet = new Dictionary <string, int>();

            while ((line = reader.ReadLine()) != null)
            {
                string[] splittedLine = line.Split(separator, stringSplitOptions);

                if (++lineNo == 1)
                {
                    for (int i = 0; i < splittedLine.Length; i++)
                    {
                        string currentColumnName = splittedLine[i];
                        headerSet.Add(currentColumnName, i);
                    }
                }
                else
                {
                    T parsedObject = new T();

                    PropertyInfo[] properties = typeof(T).GetProperties();

                    foreach (PropertyInfo property in properties)
                    {
                        ParserAttributes attribute = Attribute.GetCustomAttribute(property, typeof(ParserAttributes)) as ParserAttributes;

                        if (attribute != null)
                        {
                            var currentColumn = headerSet.FirstOrDefault(header => header.Key == attribute.CsvName);
                            if (currentColumn.Key != null)
                            {
                                int    numberOfCurrentColumn = currentColumn.Value;
                                string currentColumnValue    = splittedLine[numberOfCurrentColumn];

                                Type   propType             = property.PropertyType;
                                object convertedColumnValue = Convert.ChangeType(currentColumnValue, propType, CultureInfo.InvariantCulture);

                                property.SetValue(parsedObject, convertedColumnValue);
                            }
                        }
                    }

                    listOfObjects.Add(parsedObject);
                }
            }
        }

        return(listOfObjects);
    }
    /// <summary>
    /// Get PropertyInfo, which has attribute name equals to key
    /// </summary>
    /// <param name="propertyInfos">All PropertiesInfo in class</param>
    /// <param name="key">Attribute name</param>
    /// <returns>PropertyInfo for property with key attribute</returns>
    private PropertyInfo GetKeyPropertyInfoByCsvName(PropertyInfo[] propertyInfos, string key)
    {
        foreach (PropertyInfo property in propertyInfos)
        {
            ParserAttributes attribute = Attribute.GetCustomAttribute(property, typeof(ParserAttributes)) as ParserAttributes;
            if (attribute != null && attribute.CsvName.Equals(key))
            {
                return(property);
            }
        }

        return(null);
    }
 /// <summary>
 /// Comparation TBase and and TMergeData, to set empty properties in TBase
 /// </summary>
 /// <typeparam name="TMergeData"></typeparam>
 /// <typeparam name="TBase"></typeparam>
 /// <param name="data"></param>
 /// <param name="currentBaseRecord"></param>
 private void CompareTBaseAndTMergeDataAndFillEmptyProperties <TMergeData, TBase>(TMergeData data, TBase currentBaseRecord)
 {
     if (data != null)
     {
         foreach (PropertyInfo property in _propertiesOfBaseObject)
         {
             if (property != _keyPropertyOfBaseObject || property.GetValue(currentBaseRecord) == default)
             {
                 ParserAttributes attribute = Attribute.GetCustomAttribute(property, typeof(ParserAttributes)) as ParserAttributes;
                 SetBaseWithNewValueIfMergeDataContainsProperty(attribute, property, currentBaseRecord, data);
             }
         }
     }
 }
 private void SetBaseWithNewValueIfMergeDataContainsProperty <TBase, TMergeData>(ParserAttributes attribute, PropertyInfo property, TBase currentBaseRecord, TMergeData data)
 {
     if (attribute != null)
     {
         foreach (PropertyInfo mergeProperty in _propertiesOfObjectToMerge)
         {
             if (mergeProperty != _keyPropertyOfMergeDataObject)
             {
                 ParserAttributes mergeAttribute = Attribute.GetCustomAttribute(mergeProperty, typeof(ParserAttributes)) as ParserAttributes;
                 if (mergeAttribute != null && attribute.CsvName.Equals(mergeAttribute.CsvName))
                 {
                     var mergePropertyValue = mergeProperty.GetValue(data);
                     property.SetValue(currentBaseRecord, mergePropertyValue);
                 }
             }
         }
     }
 }