Search code examples
c#genericsgeneric-method

C# Accessing generic Method without knowing specific type


I have a method that converts List<T> to the DataTable using reflection. I want to leverage that method for creating DataSet by passing multiple lists where each list may hold different Type of object.

Below is the code that gives me compile time error:

"The type arguments for method 'ExportToExcel.CreateExcelFile.ListToDataTable<T>(System.Collections.Generic.List<T>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. " - at the line "var dt = Util.ListToDataTable(item);"

public static class Util
    {
        public static DataSet GetDataSet(List<string> title, List<IList> data)
        {
            DataSet ds = new DataSet();
            int idx= 0;
            foreach (var item in data)
            {
                //here I get compile time error "The type arguments for method
                // 'ExportToExcel.CreateExcelFile.ListToDataTable<T>
                // (System.Collections.Generic.List<T>)' cannot be inferred from the usage.
                // Try specifying the type arguments explicitly. "
                var dt = Util.ListToDataTable(item);
                if (title.Count >= idx)
                {
                    dt.TableName = title[idx];
                }
                idx++;
                ds.Tables.Add(dt);
            }
            return ds;
        }

        public static System.Data.DataTable ListToDataTable<T>(List<T> list)
        {
            var dt = new System.Data.DataTable();
            foreach (PropertyInfo info in typeof(T).GetProperties())
            {
                dt.Columns.Add(new DataColumn(info.Name, info.PropertyType));
            }
            foreach (T t in list)
            {
                DataRow row = dt.NewRow();
                foreach (PropertyInfo info in typeof(T).GetProperties())
                {
                    row[info.Name] = info.GetValue(t, null);
                }
                dt.Rows.Add(row);
            }
            return dt;
        }

    }

And this is my calling testMethod

       [TestMethod]
        public void TestDataSetGeneration_WithMultipleLists()
        {
            IList list = new List<User>();
            list.Add(new User(){FirstName = "Mahesh", LastName = "Chaudhari", IsExternal = true, UpdatedOn = DateTime.Now});
            list.Add(new User(){FirstName = "Mahesh1",LastName = "Chaudhari1",IsExternal = true,UpdatedOn = DateTime.Now});
            list.Add(new User(){FirstName = "Mahesh2",LastName = "Chaudhari2",IsExternal = false,UpdatedOn = DateTime.Now});

            IList hcps = new List<HCPUser>() { new HCPUser(){FirstName = "HCP1",LastName = "HCP1"}};

            var lists = new List<IList>();
            lists.Add(list);
            lists.Add(hcps);

            var titles = new List<String> { "Users", "HCPs"};
            var result = Util.GetDataSet(titles ,lists );
            Assert.IsTrue(result != null);
        }

I think the method Util.ListToDataTable needs specific type which it gets only at runtime. In such scenario how would I call this method?


Solution

  • It seems that the code is slightly more complex than needed. You end with with a generic type not equaling a generic type. What is wrong with simply having a IEnumerable as asked in the previous answer? The code below passes your unit test.

        public static DataSet GetDataSet(List<string> title, IEnumerable<IEnumerable> data)
        {
            DataSet ds = new DataSet();
            int idx = 0;
            foreach (var item in data)
            {
                //here I get compile time error "The type arguments for method
                // 'ExportToExcel.CreateExcelFile.ListToDataTable<T>
                // (System.Collections.Generic.List<T>)' cannot be inferred from the usage.
                // Try specifying the type arguments explicitly. "
                var dt = Util.ListToDataTable(item);
                if (title.Count >= idx)
                {
                    dt.TableName = title[idx];
                }
                idx++;
                ds.Tables.Add(dt);
            }
            return ds;
        }
    
        public static System.Data.DataTable ListToDataTable(IEnumerable list)
        {
            var dt = new System.Data.DataTable();
            var itm = list.OfType<object>().FirstOrDefault();
            if (itm == null)
                return dt;
            var typeProperties = itm.GetType().GetProperties();
    
            foreach (PropertyInfo info in typeProperties)
            {
                dt.Columns.Add(new DataColumn(info.Name, info.PropertyType));
            }
            foreach (var t in list)
            {
                DataRow row = dt.NewRow();
                foreach (PropertyInfo info in typeProperties)
                {
                    row[info.Name] = info.GetValue(t, null);
                }
                dt.Rows.Add(row);
            }
            return dt;
        }