我有一个IEnumerable<T>
参数的方法
T
可以是built-in .NET types之一,例如int
或string
,也可以是这样的自定义类:
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
是自定义类,则循环浏览属性只能正常工作
例如,它T
是string
,我得到了string
(Chars
和Length
)的属性,这对我来说毫无用处这个案例。
这意味着只检查它是否是原始类型是不够的 - 我需要识别DateTime
和GUID
之类的东西,而且可能还有更多。
(我必须承认,我没有注意到DateTime
不在内置类型列表中。)
所以我想我真正想要的是:
判断T
是否具有我可以循环的用户定义属性
(无论是否 int
都没有属性,或者我不关心的属性 string
)
这有意义吗?
但是,我仍然不确定选择哪个答案
driis'和Jon Skeet的回答都意味着我基本上必须列出很多类型(Jon的答案多于driis'的回答)。
目前,我倾向于选择Ron Sijm的答案(即使人们显然更喜欢其他答案),因为我想简单地检查"System."
是做我想要的最短路,即使看起来不对,那么优雅......
答案 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。要包含的类型的头脑风暴会出现Guid
,Decimal
,string
和DateTime
。我不认为有太多其他人,但我可能错了。
当然,直接驻留在系统命名空间中的类型是一种简单的方法,但是当有人第一次向您传递System.AppDomain
或System.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
单
双
十进制
日期时间
串
值得注意的遗漏是Guid
,DateTimeOffset
和可空类型(尽管您可以使用typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>)
检查对象是否可以为空,然后使用Nullable.GetUnderlyingType(typeof(T))
方法获取非可空类型,适用于GetTypeCode()
。
答案 4 :(得分:0)
我很难决定我是否更喜欢驾驶'或Jon的答案,但最后我选择接受Jon的回答。
对于任何有兴趣的人,我想详细说明为什么我认为这个答案对我来说是最好的解决方案:
乍一看,我比.IsPrimitive
更好地填充所有类型的HashSet
。
但是,一旦提到string
和datetime
这样的特殊情况,很明显我无论如何都不会像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>
的其他原因,这可能不是最优雅的解决方案,但我认为这至少是我的最佳解决方案现在使用案例。