如何告诉IEnumerable从IEnumerable

本文关键字:IEnumerable MyClass 何告诉 int | 更新日期: 2023-09-27 18:13:42

我有一个带有IEnumerable<T>参数的方法。
T可以是内置的。net类型之一,如intstring,也可以是这样的自定义类:

class MyClass
{
    public string Foo{ get; set; }
    public int Bar{ get; set; }
}

如果T是内置类型之一,我如何以编程方式识别?

我知道我可以这样做:

switch (typeof(T).Name.ToLower())
{
    case "int":          
    case "string":
    case "...":   // and so on...
        Console.WriteLine("It's a built-in type!");
        break;
    default:
        Console.WriteLine("It's a custom class!");
        break;
}

…但一定有更短/更简单的方法,对吗?


编辑:

好的,非常感谢你们到目前为止的回答。
但我还是不确定哪一个最适合我的情况。

我真正想做的是:
我正在写一个将IEnumerable<T>s转换为ADODB.Recordsets的库。
在每次转换开始时,我需要创建一个空的Recordset并向其中添加字段。

如果T是一个自定义类,我必须遍历它的属性,并在记录集中为T的每个属性创建一个字段(带有属性的名称和类型)。

但是,只有当T是自定义类时,循环遍历属性才能正常工作。
例如,如果Tstring,我得到string (CharsLength)的属性,在这种情况下对我没有用处。

这意味着仅仅检查它是否是原始类型是不够的——我还需要识别DateTimeGUID这样的东西,可能还有更多。
(我不得不承认,我没有注意到DateTime不在内置类型列表中)。

所以我想我真正想要的是:
告诉T是否有用户定义的属性,我可以循环,或不。
(不管它是否完全没有属性,比如 int ,或者我不关心的属性,比如 string )
这有道理吗?

然而,我仍然不确定该选哪个答案。
driis和Jon Skeet的答案都意味着我基本上必须列出很多类型(Jon的答案比driis的答案更多)。
目前,我倾向于选择Ron Sijm的答案(尽管人们显然更喜欢其他答案),因为我想简单地检查"System."是做我想做的事情的最短方法,即使它看起来不那么优雅……

如何告诉IEnumerable<int>从IEnumerable<MyClass>

这取决于你定义的"内置类型"。在许多情况下,您可以查看类型是基本类型还是字符串类型。(因为string被认为是"内置的",但它不是基本类型)。

if (typeof(T).IsPrimitive || typeof(T) == typeof(string))
    Console.WriteLine("It's a built-in type");

如果您对这些原语(来自MSDN)感到满意,这可以工作:

基本类型有:Boolean、Byte、SByte、Int16、UInt16、Int32、UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, Single.

请记住,类型名int只是Int32的c#别名。

<标题>编辑

为了确定可以直接用作记录集中字段的类型,我可能会首先查看IsPrimitive,然后有一个由ADO直接支持的其他"单值类型"的HashSet。头脑风暴的类型包括出现Guid, Decimal, stringDateTime。我不认为有太多的人,但我可能是错的。

当然,直接驻留在System名称空间中的类型将是一种简单的方法,但是当有人第一次传递给您System.AppDomainSystem.Uri时,您将遇到麻烦。基本上,如果您查看System名称空间中的内容,就会发现绝大多数类型都不应该放在单个字段中。

您确定的"内置类型"可能是特定于上下文的—例如,您已经列出了c#语言内置的类型,但真正的特定于c#。例如,它包括decimal(它不是CLR原语类型),但不包括DateTime(其他语言可以显式支持)。

那么,我就使用HashSet<Type>,你可以适当地创建它:

private static readonly HashSet<Type> BuiltInTypes = new HashSet<Type>
{
    typeof(object), typeof(string), typeof(byte), typeof(sbyte),
    // etc
};
// Then:
if (BuiltInTypes.Contains(typeof(T)))
{
    Console.WriteLine("It's a built-in type!");
}
else
{
    Console.WriteLine("It's a custom class!");
}

您可以使用(typeof(T).FullName.StartsWith("System."))

我喜欢使用Type.GetTypeCode()来处理数据库'原语'。您可以使用一个简单的条件来告诉您它是否是数据库原语,如下所示:

bool isDBPrimitive = Type.GetTypeCode(typeof(T)) != Object;

GetTypeCode()的返回类型是TypeCode枚举,如果类型匹配,则返回以下枚举值之一,否则返回TypeCode.Object枚举值:

DBNull
布尔
字符
SByte
字节
Int16
UInt16
Int32
UInt32
Int64
UInt64


十进制
DateTime
字符串

值得注意的遗漏是Guid, DateTimeOffset和可空类型(尽管您可以使用typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>)来检查对象是否可空,然后使用Nullable.GetUnderlyingType(typeof(T))方法来获得非可空类型,这将与GetTypeCode()一起工作。

我很难决定我更喜欢driis的答案还是Jon的答案,但最后我选择了接受Jon的答案。

对于感兴趣的人,我想详细说明一下为什么我认为这个答案对我来说是最好的解决方案:

乍一看,我更喜欢.IsPrimitive,而不是用所有类型填充HashSet
但是一旦像stringdatetime这样的特殊情况被提到,很明显,我无论如何也不会像if (typeof(T).IsPrimitive)这样的简短解决方案。

所以我更多地考虑了我的实际用例(从IEnumerable<T>转换到ADODB.Recordset),我注意到,对于CLR类型到ADO类型的实际转换,我需要类型的列表。
(知道Int16需要转换为ADODB.DataTypeEnum.adSmallInt,等等)

所以最后我做了一些类似Jon建议的事情,但我使用Dictionary来保存我支持的CLR类型相应的ADO类型:
internal static class DataTypes
{
    private static readonly Dictionary<Type, ADODB.DataTypeEnum> Types
        = new Dictionary<Type, ADODB.DataTypeEnum>()
        {
            { typeof(Boolean), ADODB.DataTypeEnum.adBoolean },
            { typeof(DateTime), ADODB.DataTypeEnum.adDate },
            // and so on...
        };
    public static bool TryGetAdoTypeForClrType(Type clrType, out ADODB.DataTypeEnum adoType)
    {
        return Types.TryGetValue(clrType, out adoType);
    }
}

我可以在稍后将POCO属性转换为ADO字段的实际工作中使用它。

在我开始转换之前,我还可以检查IEnumerable<T>T,因为如果TryGetAdoTypeForClrType(typeof(T), ...)找到了一些东西,那么T不能是POCO/自定义类。

这可能不是最优雅的解决方案,如果你需要区分IEnumerable<int>IEnumerable<MyClass>的其他原因比我做的,但我认为它至少是最好的解决方案,为我的用例现在