2015-01-15 4 views
2

У меня есть метод, который преобразует List<T> в DataTable с использованием отражения. Я хочу использовать этот метод для создания DataSet, передавая несколько списков, где каждый список может содержать другой тип объекта.C# Доступ к универсальному методу без знания определенного типа

Ниже приведен код, который дает мне компиляции ошибки времени:

- в строке "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; 
     } 

    } 

И это я называю 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); 
     } 
" 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."

Я думаю, что метод Util.ListToDataTable требует определенного типа, который он получает только во время выполнения. В таком сценарии, как я могу назвать этот метод?

+0

Ирония в этом вопросе: у меня есть код, который делает то же самое (в основном), но я делаю это совершенно по-другому. Хороший Q, кстати, очень хорошо написан. – theMayer

+0

Используете ли вы WPF? Или WinForms? – theMayer

+0

@ theMayer: этот метод написан в библиотеке классов C#, передаваемой по проекту WebAPI – Mahesh

ответ

1

Кажется, что код немного сложнее, чем необходимо. Вы заканчиваете с общим типом, не равным родовому типу. Что не так, просто имея IEnumerable, как было сказано в предыдущем ответе? Приведенный ниже код проходит ваш модульный тест.

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; 
    } 
3

Вы можете создать общий объект MethodInfo с использованием MethodInfo.MakeGenericMethod и вызвать это через отражение, но я не вижу причин, по которым метод ListToDataTable должен быть общим.

Одним из вариантов было бы изменить List<T> на что-то не общее, как IEnumerable. Однако вам нужно будет узнать тип, получив первый элемент из списка.

Другим вариантом было бы LINQ:

public static System.Data.DataTable ListToDataTable<T>(object[] list) 
{ 
    .... 
} 

var dt = ListToDataTable(item.Cast<object>().ToArray()); 

Оба варианта не смогут узнать тип, если список пуст. Возможно, кто-то еще имеет более аккуратный способ.

Редактирование: теперь я видел, что ваши данные являются не общим IList с самого начала, так что тип в основном уже потерян.

+0

согласен «но я не вижу причин, по которым метод ListToDataTable должен быть общим». Я мог бы сделать это с помощью IEnumerable, и это сработало. – Mahesh

Смежные вопросы