Reputation: 22595
I have a method that returns a List of objects that implement an interface:
private List<IFoo> GetData(string key)
{
...returns a different concrete implementation depending on the key
switch (key)
{
case "Bar":
return new List<Bar>();//Bar:IFoo
break;
case "Foo":
return new List<Foo>();//Foo:IFoo
break;
case "FooBar":
return new List<FooBar>();//FooBar:IFoo
break;
//etc etc - (quite a lot of these)
}
}
And I want to convert the result to a DataTable:
var result = GetData("foobar");
return ConvertToDataTable(result)
and my implementation of ConvertToDataTable looks something like this:
private DataTable ConvertToDataTable<T>(IEnumerable<T> data)
{
//problem is typeof(T) is always IFoo - not FooBar
PropertyInfo[] properties = typeof(T).GetProperties();
DataTable table = new DataTable();
foreach (var prop in properties)
{
table.Columns.Add(prop.DisplayName, prop.PropertyType);
}
//etc..
}
How can I get the underlying type in the generic ConvertToDataTable method?
Upvotes: 1
Views: 2249
Reputation: 9168
GetType() is what gets you the concrete class at runtime. The answer you accepted is a good solution for the question you asked.
Now, from the point of view of what you're trying to accomplish, I wanted to offer that creating your DataTable doesn't really require that RTTI. Here's an implementation of your ConvertToDataTable method that "doesn't care" what T is, as long as it implements IFoo.
private static DataTable ConvertToDataTable<T>(IEnumerable<T> data)
{
// Reflect the properties from T which is IFoo
PropertyInfo[] properties = typeof(T).GetProperties();
DataTable table = new DataTable();
// Add columns
foreach (var prop in properties)
{
table.Columns.Add(
prop.Name,
prop.PropertyType
).DataType = prop.PropertyType;
}
Console.WriteLine("Inside the generic method: ");
// Add rows
foreach (var item in data)
{
// RE: For "the question you asked": Use GetType() for object info.
Console.WriteLine("...the concrete Type is " + item.GetType().Name);
// I would ask, though, do you really need it for anything here?
// But for "the thing you're trying to accomplish" (making a DataTable)
// - This goes by the public properties declared in the interface IFoo.
// - It pulls properties GENERICALLY for ANY class that implements IFoo.
object[] values =
properties.Select(property => property.GetValue(item)).ToArray();
table.Rows.Add(values);
}
return table;
}
It picks up whatever is declared in the IFoo interface:
internal interface IFoo
{
int ID { get; }
string Name { get; }
string Text { get; set; }
}
It works to pass in IEnumerable containing completely different classes because they both implement IFoo:
class FooA : IFoo
{
public int ID { get; } = 1;
public string Name { get; } = "I am Foo A";
public string Text { get; set; }
}
class FooB : IFoo
{
public int ID { get; } = 2;
public string Name { get; } = "I am Foo B";
public string Text { get; set; }
}
Console Output:
Inside the generic method:
...the concrete Type is FooA
...the concrete Type is FooB
D I S P L A Y P O P U L A T E D T A B L E
ID Name Text
1 I am Foo A
2 I am Foo B
You can download from our GitHub if you want to try it out.
Upvotes: 1
Reputation: 949
Replace typeof which is evaluated at compileTime by .GetType which is evaluated at runtime and you will get the coorect type, not the interface:
private DataTable ConvertToDataTable<T>(IEnumerable<T> data)
{
Type dataType;
if (data != null && data.Count() != 0)
{
//problem is typeof(T) is always IFoo - not FooBar
//typeof(T) will always return IFoo
//Will return the correct type
dataType = data.First().GetType();
}
else
{
return new DataTable();
//or throw ?
}
PropertyInfo[] properties = dataType.GetProperties();
DataTable table = new DataTable();
foreach (var prop in properties)
{
table.Columns.Add(prop.DisplayName, prop.PropertyType);
}
//etc..
}
Upvotes: 3
Reputation: 947
using System;
using System.Collections.Generic;
using System.Data;
namespace StackOverflow001
{
class Program
{
static void Main(string[] args)
{
var data = GetData("Foo");
var table = ConvertToDataTable(data);
data = GetData("Bar");
table = ConvertToDataTable(data);
data = GetData("FooBar");
table = ConvertToDataTable(data);
}
static IEnumerable<FooBase> GetData(string key) =>
key switch
{
"Foo" => new List<Foo>(),
"Bar" => new List<Bar>(),
"FooBar" => new List<FooBar>(),
_ => throw new ArgumentException(nameof(key)),
};
static DataTable ConvertToDataTable(IEnumerable<FooBase> data)
{
var properties = data switch
{
List<Foo> _ => typeof(Foo).GetProperties(),
List<Bar> _ => typeof(Bar).GetProperties(),
List<FooBar> _ => typeof(FooBar).GetProperties(),
_ => throw new ArgumentException(nameof(data)),
};
DataTable table = new DataTable();
foreach (var prop in properties)
{
table.Columns.Add(prop.Name, prop.PropertyType);
}
return table;
}
}
interface IFoo {}
abstract class FooBase : IFoo { }
class Foo : FooBase { public int FooProp { get; set; } }
class Bar : FooBase { public int BarProp { get; set; } }
class FooBar : FooBase { public int FooBarProp { get; set; }}
}
I think that using interface and generic methods is a bad idea in this situation. Using inheritance can make your code much easier and cleaner.
Upvotes: 0