C# 重温IEnumerable到DataTable扩展方法——字符串问题

C# 重温IEnumerable到DataTable扩展方法——字符串问题,c#,string,datatable,ienumerable,extension-methods,C#,String,Datatable,Ienumerable,Extension Methods,我正在开发一个可以在IEnumerable上使用的“ToDataTable()”。很多这样的例子,比如这里:。数据表对我来说很理想,因为我来自一个Foxpro领域,我们习惯于使用游标。所以,我之所以挖掘数据表,是因为它们相对简单,易于使用,并且可以容纳像样的列元数据。我已经有了一堆代码,可以很好地显示它们(带有排序和过滤网格标题)、导出到Excel等等 有人对我上面链接的线程做了一个非常好的评论:“如果类型是string,那么这不起作用,因为属性是chars和length,但是get值会尝试将完

我正在开发一个可以在IEnumerable上使用的“ToDataTable()”。很多这样的例子,比如这里:。数据表对我来说很理想,因为我来自一个Foxpro领域,我们习惯于使用游标。所以,我之所以挖掘数据表,是因为它们相对简单,易于使用,并且可以容纳像样的列元数据。我已经有了一堆代码,可以很好地显示它们(带有排序和过滤网格标题)、导出到Excel等等

有人对我上面链接的线程做了一个非常好的评论:“如果类型是string,那么这不起作用,因为属性是chars和length,但是get值会尝试将完整字符串放入char列。”

问题是,某些东西适合扩展方法调用(如字符串的一维数组),但在转换发生时会抛出错误。我编写了一个扩展方法,将IsValueType()添加到混合中,但问题是字符串返回false。最后,我添加了两个kludges:一个参数,用于强制将序列中的项视为值;如果发现序列中的项属于“String”类型,则将其视为值

有没有更好的方法来实现这一点,或者字符串仅仅是一个奇怪的人,因为他们独特的类型的键入和反射结果?下面的代码适用于我能想到的每一个IEnumerable,它适用于除string之外的所有DataTable列类型的一维数组(好吧,它适用于带有硬编码的kludge的字符串,但在其他情况下会出错)。对字符串场景进行硬编码并没有什么大不了的,我只是希望有一种更优雅的方式来实现这一点。有什么想法吗

    public static DataTable ToDataTable<T>(this IEnumerable<T> items, string tableName = "", bool treatItemAsValue = false)
    {
        // We want a single extension method that can take in an enumerable sequence (such as a LINQ query)
        // and return the result as a DataTable. We want this to be a one stop shop for converting
        // various objects into DataTable format, as DataTables are a nice parallel to Foxpro cursors.
        if (items == null) { return null; }
        Type itemType = typeof(T);
        bool typeIsNullable = itemType.IsGenericType && typeof(T).GetGenericTypeDefinition().Equals(typeof(Nullable<>));
        string itemTypeName = "";
        bool typeIsValue = false;
        Type itemUnderlyingType = itemType;
        if (typeIsNullable)
        {
            // Type of enumerable item is nullable, so we need to find its base type.
            itemUnderlyingType = Nullable.GetUnderlyingType(itemType);
        }
        typeIsValue = itemUnderlyingType.IsValueType;
        itemTypeName = itemUnderlyingType.Name;
        DataTable dt = new DataTable();
        DataColumn col = null;
        if ((treatItemAsValue) || (itemTypeName == "String"))
        {
            // We have been asked to treat the item in the sequence as a value, of the items
            // in the sequence are strings. Strings are NOT considered a value type in regards
            // to IsValueType(), but when item values are assessed, it will be the value
            // of the string that tries to pull in.
            typeIsValue = true;
        }
        if (itemTypeName == "DataRow")
        {
            // Special case. If our enumerable type is DataRow, then we can utilize a more appropriate
            // (built-in) extension method to convert enumerable DataRows to a DataTable.
            dt = ((IEnumerable<DataRow>)items).CopyToDataTable();
        }
        else
        {
            // We must have an enumerable sequence/collection of some other type, possibly anonymous.
            // Get properties of the enumerable to add as columns to the data table.
            if (typeIsValue)
            {
                // Our enumerable items are of a value type (e.g. integers in a one-dimensional array).
                col = dt.Columns.Add();
                col.AllowDBNull = typeIsNullable;
                col.ColumnName = itemTypeName;
                col.DataType = itemUnderlyingType;
                // Now walk through the enumeration and add rows to our data table (single values).
                foreach (var item in items)
                {
                    dt.Rows.Add(item);
                }
            }
            else
            {
                // The type should be something we can walk through the properties of in order to
                // generate properly named and typed columns of our DataTable.
                PropertyInfo[] props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
                foreach (var prop in props)
                {
                    Type propType = prop.PropertyType;
                    // Is it a nullable type? Get the underlying type.
                    if (propType.IsGenericType && propType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
                    {
                        propType = new NullableConverter(propType).UnderlyingType;
                    }
                    dt.Columns.Add(prop.Name, propType);
                }
                // Now walk through the enumeration and add rows to our data table.
                foreach (var item in items)
                {
                    var values = new object[props.Length];
                    for (int i = 0; i < props.Length; i++)
                    {
                        values[i] = props[i].GetValue(item, null);
                    }
                    dt.Rows.Add(values);
                }
            }
        }
        // Give the DataTable a reasonable name.
        if (tableName.Length == 0)
        {
            if (typeof(T).IsAnonymous())
            {
                // Anonymous types have really goofy names, so there is no use using that as table name.
                tableName = "Anonymous";
            }
            else
            {
                // This is NOT an anonymous type, so we can use the type name as table name.
                tableName = typeof(T).Name;
            }
        }
        return dt;
    }
public static DataTable ToDataTable(此IEnumerable items,字符串tableName=“”,bool treatItemAsValue=false)
{
//我们需要一个可以接受可枚举序列的扩展方法(例如LINQ查询)
//并将结果作为DataTable返回。我们希望这是转换的一站式服务
//将各种对象转换为DataTable格式,因为DataTables与Foxpro游标非常相似。
如果(items==null){returnnull;}
类型itemType=typeof(T);
bool-typeIsNullable=itemType.IsGenericType&&typeof(T).GetGenericTypeDefinition().Equals(typeof(Nullable));
字符串itemTypeName=“”;
bool typeIsValue=false;
类型ItemUnderlineType=itemType;
if(typeIsNullable)
{
//可枚举项的类型是可空的,所以我们需要找到它的基类型。
ItemUnderlinkType=Nullable.GetUnderlinkType(itemType);
}
typeIsValue=itemUnderlyingType.IsValueType;
itemTypeName=itemUnderlyingType.Name;
DataTable dt=新的DataTable();
DataColumn=null;
if((treatItemAsValue)| |(itemTypeName==“字符串”))
{
//我们被要求将序列中的项目视为项目的值
//序列中有字符串。字符串不被视为值类型
//到IsValueType(),但在评估项目值时,它将是
//试图拉进去的绳子。
typeIsValue=true;
}
如果(itemTypeName==“DataRow”)
{
//特殊情况。如果我们的可枚举类型是DataRow,那么我们可以使用更合适的
//(内置)扩展方法,用于将可枚举数据行转换为数据表。
dt=((IEnumerable)项).CopyToDataTable();
}
其他的
{
//我们必须有一个其他类型的可枚举序列/集合,可能是匿名的。
//获取要作为列添加到数据表的枚举的属性。
if(typeIsValue)
{
//我们的可枚举项属于值类型(例如,一维数组中的整数)。
col=dt.Columns.Add();
col.AllowDBNull=类型为空;
col.ColumnName=项目类型名称;
col.DataType=ItemUnderlineType;
//现在遍历枚举并将行添加到数据表(单个值)。
foreach(项目中的var项目)
{
dt.行。添加(项目);
}
}
其他的
{
//类型应该是我们可以遍历的属性,以便
//生成DataTable中正确命名和类型化的列。
PropertyInfo[]props=typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach(道具中的var道具)
{
类型propType=prop.PropertyType;
//它是可为空的类型吗?获取基础类型。
if(propType.IsGenericType&&propType.GetGenericTypeDefinition().Equals(typeof(null)))
{
propType=新的NullableConverter(propType).UnderlinedType;
}
dt.Columns.Add(prop.Name,propType);
}
//现在遍历枚举并将行添加到数据表中。
foreach(项目中的var项目)
{
var值=新对象[props.Length];
for(int i=0;i    public static DataTable ToDataTable<T>(this IEnumerable<T> items, string tableName = "", bool treatItemAsValue = false)
    {
        // We want a single extension method that can take in an enumerable sequence (such as a LINQ query)
        // and return the result as a DataTable. We want this to be a one stop shop for converting
        // various objects into DataTable format, as DataTables are a nice parallel to Foxpro cursors.
        if (items == null) { return null; }
        Type itemType = typeof(T);
        bool typeIsNullable = itemType.IsGenericType && typeof(T).GetGenericTypeDefinition().Equals(typeof(Nullable<>));
        string itemTypeName = "";
        bool typeIsValue = false;
        Type itemUnderlyingType = itemType;
        if (typeIsNullable)
        {
            // Type of enumerable item is nullable, so we need to find its base type.
            itemUnderlyingType = Nullable.GetUnderlyingType(itemType);
        }
        typeIsValue = itemUnderlyingType.IsValueType;
        itemTypeName = itemUnderlyingType.Name;
        DataTable dt = new DataTable();
        DataColumn col = null;
        PropertyInfo[] props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        if ((treatItemAsValue) || (itemTypeName == "String") || (props.Length == 0))
        {
            // We have been asked to treat the item in the sequence as a value, or the items
            // in the sequence is a string which cannot be "flattened" properly by analyzing properties.
            // OR, the type has no properties to put on display, so we should just use the item directly.
            // (like the base "Object" type).
            typeIsValue = true;
        }
        if (itemTypeName == "DataRow")
        {
            // Special case. If our enumerable type is DataRow, then we can utilize a more appropriate
            // (built-in) extension method to convert enumerable DataRows to a DataTable.
            dt = ((IEnumerable<DataRow>)items).CopyToDataTable();
        }
        else
        {
            // We must have an enumerable sequence/collection of some other type, possibly anonymous.
            // Get properties of the enumerable to add as columns to the data table.
            if (typeIsValue)
            {
                // Our enumerable items are of a value type (e.g. integers in a one-dimensional array).
                col = dt.Columns.Add();
                // Whether or not the type is nullable, the value might be null (e.g. for type "Object").
                col.AllowDBNull = true;
                col.ColumnName = itemTypeName;
                col.DataType = itemUnderlyingType;
                // Now walk through the enumeration and add rows to our data table (single values).
                foreach (var item in items)
                {
                    dt.Rows.Add(item);
                }
            }
            else
            {
                // The type should be something we can walk through the properties of in order to
                // generate properly named and typed columns of our DataTable.
                foreach (var prop in props)
                {
                    Type propType = prop.PropertyType;
                    // Is it a nullable type? Get the underlying type.
                    if (propType.IsGenericType && propType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
                    {
                        propType = new NullableConverter(propType).UnderlyingType;
                    }
                    dt.Columns.Add(prop.Name, propType);
                }
                // Now walk through the enumeration and add rows to our data table.
                foreach (var item in items)
                {
                    if (item != null)
                    {
                        // Can only add an item as a row if it is not null.
                        var values = new object[props.Length];
                        for (int i = 0; i < props.Length; i++)
                        {
                            values[i] = props[i].GetValue(item, null);
                        }
                        dt.Rows.Add(values);
                    }
                }
            }
        }
        // Give the DataTable a reasonable name.
        if (tableName.Length == 0)
        {
            if (typeof(T).IsAnonymous())
            {
                // Anonymous types have really goofy names, so there is no use using that as table name.
                tableName = "Anonymous";
            }
            else
            {
                // This is NOT an anonymous type, so we can use the type name as table name.
                tableName = typeof(T).Name;
            }
        }
        return dt;
    }