ジェネリックについて学んだばかりで、それを使用してクラスからデータテーブルを動的に構築できるかどうか疑問に思っています。
または、ここでポイントが欠落している可能性があります。ここに私のコードがあります。私がやろうとしているのは、既存のクラスからデータテーブルを作成し、それを設定することです。しかし、私は思考プロセスで立ち往生しています。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;
namespace Generics
{
public class Dog
{
public string Breed { get; set; }
public string Name { get; set; }
public int legs { get; set; }
public bool tail { get; set; }
}
class Program
{
public static DataTable CreateDataTable(Type animaltype)
{
DataTable return_Datatable = new DataTable();
foreach (PropertyInfo info in animaltype.GetProperties())
{
return_Datatable.Columns.Add(new DataColumn(info.Name, info.PropertyType));
}
return return_Datatable;
}
static void Main(string[] args)
{
Dog Killer = new Dog();
Killer.Breed = "Maltese Poodle";
Killer.legs = 3;
Killer.tail = false;
Killer.Name = "Killer";
DataTable dogTable = new DataTable();
dogTable = CreateDataTable(Dog);
//How do I continue from here?
}
}
}
DataTable
ポイントでエラーになりました。また、リフレクションとジェネリックが初めてなので、実際にデータにKillerクラスを入力するにはどうすればよいですか?
これまでのすべての回答に基づいて、コレクションからDataTableを作成するバージョンを次に示します。
public static DataTable CreateDataTable<T>(IEnumerable<T> list)
{
Type type = typeof(T);
var properties = type.GetProperties();
DataTable dataTable = new DataTable();
foreach (PropertyInfo info in properties)
{
dataTable.Columns.Add(new DataColumn(info.Name, Nullable.GetUnderlyingType(info.PropertyType) ?? info.PropertyType));
}
foreach (T entity in list)
{
object[] values = new object[properties.Length];
for (int i = 0; i < properties.Length; i++)
{
values[i] = properties[i].GetValue(entity);
}
dataTable.Rows.Add(values);
}
return dataTable;
}
私のお気に入りの自家製機能。すべて同時に作成および入力されます。オブジェクトを投げます。
public static DataTable ObjectToData(object o)
{
DataTable dt = new DataTable("OutputData");
DataRow dr = dt.NewRow();
dt.Rows.Add(dr);
o.GetType().GetProperties().ToList().ForEach(f =>
{
try
{
f.GetValue(o, null);
dt.Columns.Add(f.Name, f.PropertyType);
dt.Rows[0][f.Name] = f.GetValue(o, null);
}
catch { }
});
return dt;
}
David's answer のよりコンパクトなバージョンは次のとおりです。これは拡張機能でもあります。 GithubのC#プロジェクト にコードを投稿しました。
public static class Extensions
{
public static DataTable ToDataTable<T>(this IEnumerable<T> self)
{
var properties = typeof(T).GetProperties();
var dataTable = new DataTable();
foreach (var info in properties)
dataTable.Columns.Add(info.Name, Nullable.GetUnderlyingType(info.PropertyType)
?? info.PropertyType);
foreach (var entity in self)
dataTable.Rows.Add(properties.Select(p => p.GetValue(entity)).ToArray());
return dataTable;
}
}
DataTable to CSV を記述するコードと組み合わせて非常にうまく機能することがわかりました。
これを変更することでエラーを解決できます。
dogTable = CreateDataTable(Dog);
これに:
dogTable = CreateDataTable(typeof(Dog));
しかし、あなたがやろうとしていることにはいくつかの警告があります。まず、DataTable
は複雑な型を格納できないため、Dog
にCat
のインスタンスがある場合、列として追加することはできません。その場合に何をしたいかはあなた次第ですが、心に留めておいてください。
第二に、DataTable
を使用するのは、消費するデータについてnothingを知っているコードをビルドするときだけにすることをお勧めします。これには有効なユースケースがあります(例:ユーザー主導のデータマイニングツール)。 Dog
インスタンスに既にデータがある場合は、それを使用してください。
もう一つのちょっとした話、これ:
DataTable dogTable = new DataTable();
dogTable = CreateDataTable(Dog);
これに凝縮することができます:
DataTable dogTable = CreateDataTable(Dog);
以下は、datetimeフィールドのタイムゾーンの問題を修正した、少し変更されたコードです。
public static DataTable ToDataTable<T>(this IList<T> data)
{
PropertyDescriptorCollection props =
TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
for (int i = 0; i < props.Count; i++)
{
PropertyDescriptor prop = props[i];
table.Columns.Add(prop.Name, prop.PropertyType);
}
object[] values = new object[props.Count];
foreach (T item in data)
{
for (int i = 0; i < values.Length; i++)
{
if (props[i].PropertyType == typeof(DateTime))
{
DateTime currDT = (DateTime)props[i].GetValue(item);
values[i] = currDT.ToUniversalTime();
}
else
{
values[i] = props[i].GetValue(item);
}
}
table.Rows.Add(values);
}
return table;
}
これは、オブジェクトとして関数に渡された汎用リストからデータテーブルを作成するVB.Netバージョンです。オブジェクトからデータテーブルを作成するヘルパー関数(ObjectToDataTable)もあります。
System.Reflectionのインポート
Public Shared Function ListToDataTable(ByVal _List As Object) As DataTable
Dim dt As New DataTable
If _List.Count = 0 Then
MsgBox("The list cannot be empty. This is a requirement of the ListToDataTable function.")
Return dt
End If
Dim obj As Object = _List(0)
dt = ObjectToDataTable(obj)
Dim dr As DataRow = dt.NewRow
For Each obj In _List
dr = dt.NewRow
For Each p as PropertyInfo In obj.GetType.GetProperties
dr.Item(p.Name) = p.GetValue(obj, p.GetIndexParameters)
Next
dt.Rows.Add(dr)
Next
Return dt
End Function
Public Shared Function ObjectToDataTable(ByVal o As Object) As DataTable
Dim dt As New DataTable
Dim properties As List(Of PropertyInfo) = o.GetType.GetProperties.ToList()
For Each prop As PropertyInfo In properties
dt.Columns.Add(prop.Name, prop.PropertyType)
Next
dt.TableName = o.GetType.Name
Return dt
End Function
オブジェクトをxmlに変換し、xmlドキュメントをデータセットにロードしてから、データセットから最初のテーブルを抽出できます。ただし、ストリーム、データセット、およびデータテーブルの作成、および会話を使用したxmlドキュメントの作成を推測するため、これがどのように実用的かはわかりません。
概念実証のために、理由を理解できると思います。以下に例を示しますが、使用するのは少しためらいます。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;
using System.Xml.Serialization;
namespace Generics
{
public class Dog
{
public string Breed { get; set; }
public string Name { get; set; }
public int legs { get; set; }
public bool tail { get; set; }
}
class Program
{
public static DataTable CreateDataTable(Object[] arr)
{
XmlSerializer serializer = new XmlSerializer(arr.GetType());
System.IO.StringWriter sw = new System.IO.StringWriter();
serializer.Serialize(sw, arr);
System.Data.DataSet ds = new System.Data.DataSet();
System.Data.DataTable dt = new System.Data.DataTable();
System.IO.StringReader reader = new System.IO.StringReader(sw.ToString());
ds.ReadXml(reader);
return ds.Tables[0];
}
static void Main(string[] args)
{
Dog Killer = new Dog();
Killer.Breed = "Maltese Poodle";
Killer.legs = 3;
Killer.tail = false;
Killer.Name = "Killer";
Dog [] array_dog = new Dog[5];
Dog [0] = killer;
Dog [1] = killer;
Dog [2] = killer;
Dog [3] = killer;
Dog [4] = killer;
DataTable dogTable = new DataTable();
dogTable = CreateDataTable(array_dog);
// continue here
}
}
}
次の例を見てください here
@neoistheoneが提供する回答を使用して、次のセクションを変更しました。現在は正常に動作します。
DataTable dogTable = new DataTable();
dogTable = CreateDataTable(typeof(Dog));
dogTable.Rows.Add(Killer.Breed, Killer.Name,Killer.legs,Killer.tail);
foreach (DataRow row in dogTable.Rows)
{
Console.WriteLine(row.Field<string>("Name") + " " + row.Field<string>("Breed"));
Console.ReadLine();
}