如何从IEnumerable <myclass>告诉IEnumerable <int>?</myclass> </int>

时间:2011-11-06 17:49:38

标签: c# .net generics

我有一个IEnumerable<T>参数的方法 T可以是built-in .NET types之一,例如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是自定义类,我必须loop through its properties并在Recordset中为T的每个属性创建一个字段(包含属性的名称和类型)。

但是,如果T是自定义类,则循环浏览属性只能正常工作 例如,它Tstring,我得到了stringCharsLength)的属性,这对我来说毫无用处这个案例。

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

所以我想我真正想要的是:
判断T是否具有我可以循环的用户定义属性 (无论是否 int 都没有属性,或者我不关心的属性 string
这有意义吗?

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

5 个答案:

答案 0 :(得分:6)

这取决于您定义为“内置类型”的内容。在许多情况下,您可以查看类型是原始类型还是字符串。 (因为字符串被认为是“内置”,但它不是原始的。)

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

如果您对这些原语(from MSDN)感到满意,那么这是有效的:

  

基元类型是布尔,字节,SByte,Int16,UInt16,Int32,   UInt32,Int64,UInt64,IntPtr,UIntPtr,Char,Double和Single。

请记住,typename int只是Int32的C#别名。

修改

为了确定可以直接用作记录集中的字段的类型,我可能首先查看IsPrimitive,然后使用ADO直接支持的其他“单值类型”的HashSet。要包含的类型的头脑风暴会出现GuidDecimalstringDateTime。我不认为有太多其他人,但我可能错了。

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

答案 1 :(得分:5)

您确定为“内置类型”的内容可能是特定于上下文的 - 例如,您列出了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!");
}

答案 2 :(得分:0)

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

答案 3 :(得分:0)

我喜欢使用Type.GetTypeCode()来处理数据库&#39;原语&#39;。您可以有一个简单的条件,告诉您它是否是数据库原语:

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

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

  

的DBNull
  布尔
  字符
  为SByte
  字节
  INT16
  UINT16
  INT32
  UInt32的
  Int64的
  UINT64
  单
  双
  十进制
  日期时间
  串

值得注意的遗漏是GuidDateTimeOffset和可空类型(尽管您可以使用typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>)检查对象是否可以为空,然后使用Nullable.GetUnderlyingType(typeof(T))方法获取非可空类型,适用于GetTypeCode()

答案 4 :(得分:0)

我很难决定我是否更喜欢驾驶'或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字段的实际工作。

在开始转换之前,我还可以检查T的{​​{1}},因为如果IEnumerable<T>找到了某些内容,那么TryGetAdoTypeForClrType(typeof(T), ...)就不能成为POCO / custom类。

如果你需要告诉T来自IEnumerable<int>的其他原因,这可能不是最优雅的解决方案,但我认为这至少是我的最佳解决方案现在使用案例。