检查对象是不是为值类型的最有效方法
Posted
技术标签:
【中文标题】检查对象是不是为值类型的最有效方法【英文标题】:Most efficient way to check if an object is a value type检查对象是否为值类型的最有效方法 【发布时间】:2011-08-10 13:22:59 【问题描述】:警告:此代码很烂,请参阅 ANTHONY 的评论
哪个更快?
1.
public bool IsValueType<T>(T obj)
return obj is ValueType;
2.
public bool IsValueType<T>(T obj)
return obj == null ? false : obj.GetType().IsValueType;
3.
public bool IsValueType<T>(T obj)
return default(T) != null;
4.别的东西
【问题讨论】:
性能真的很重要吗,因为它真的是微优化 所写的方法 2 和 3 无效。obj == null ||
将为引用类型返回 true。 default(T) != null
将为 Nullable<T>
结构返回 false。
您对方法 2 的编辑仍然无效。 obj != null ||
将为非空引用类型的对象返回 true。
由于我过于挑剔,呃,我的意思是有帮助,方法 1 不喜欢空的 Nullable<T>
对象。 int? bar = null;
将其传递给函数,你会得到错误的。 (说实话,没想到会这样。)
方法 2 最新编辑。 return obj == null ? false : ...
仍然给 Nullable<T>
带来问题。
【参考方案1】:
您并没有真正测试一个对象——您想要测试类型。要调用它们,调用者必须知道类型,但是……嗯。给定签名<T>(T obj)
唯一理智的答案是:
public bool IsValueType<T>()
return typeof(T).IsValueType;
或者如果我们想使用示例对象进行类型推断:
public bool IsValueType<T>(T obj)
return typeof(T).IsValueType;
这不需要拳击(GetType()
是拳击),并且Nullable<T>
没有问题。一个更有趣的例子是当你传递object
...
public bool IsValueType(object obj);
在这里,null
已经存在大量问题,因为它可能是一个空的 Nullable<T>
(一个结构)或一个类。但一个合理的尝试是:
public bool IsValueType(object obj)
return obj != null && obj.GetType().IsValueType;
但请注意,空Nullable<T>
s 是不正确的(并且无法修复)。在这里,担心拳击变得毫无意义,因为我们已经被装箱了。
【讨论】:
有没有办法绕过IsValueType
属性?我正在使用不支持此属性的 .NET DNX。
typeof(ValueType).IsAssignableFrom(t)
也不起作用。
@Shimmy -- 在 OP 的代码中 -- if (default(T) != null)
应该可以工作。
在 DNX / .NET Core 中,您可以这样做 typeof(your_type).GetTypeInfo().IsValueType
。【参考方案2】:
我的第一个答案是编写一个简单的测试并自己找出答案。
我的第二个答案(当然,我没有进行任何测试)是选项 1。这是最简单的检查。第二种方法涉及两个单独的检查,而第三种方法涉及创建一个类型的默认实例。
您还应该考虑可读性。该框架已经使您能够在代码中包含以下内容:
if(someObj is ValueType)
// Do some work
为什么还要创建一个简单地将上述语句转换为的方法(假设您将方法设为静态并允许编译器推断泛型类型):
if(IsValueType(someObj))
// Do some work
【讨论】:
谢谢 - 我并不是在提倡创建一种测试它的方法。我只是这样写是为了清楚我在问什么(someObj is ValueType)
对于无效的Nullable<T>
似乎有问题。我不是 IL 大师,但我相信这涉及到拳击,这在这种情况下效果不佳。
你怎么知道is
是最简单的检查?例如,您可以测试一个对象是否用它实现了一个接口,这不是那么“简单”。不知道编译器如何处理它,但请查看 IsAssignableFrom 和 ImplementInterface (由它调用)是如何实现的。您是否了解更多,或者您只是认为is
更快,因为它看起来更简单?【参考方案3】:
定义结构实际上定义了两种类型:值类型和派生自System.ValueType
的类类型。如果请求创建派生自 System.ValueType 的类型的变量、参数、字段或数组(统称为“存储位置”),则系统将改为创建一个存储位置来存储对象的字段,而不是存储对出现这些字段的对象的引用。另一方面,如果请求创建派生自 System.ValueType 的类型的实例,系统将创建派生自 System.ValueType 的类的对象实例。
这可以通过创建一个实现 IValue 的结构来证明:
接口 IValue int 值 get;放;; 结构值结构:IValue 公共 int 值 get;放;;使用通用测试例程和代码来包装它:
static void Test请注意,在任何情况下,对传递给 Test 的参数调用 GetType 都会产生 ValueStruct,它会将自身报告为值类型。尽管如此,传入的项目在前两次调用中只会是“真实”值类型。在第三次和第四次调用中,它实际上是一个类类型,正如对duplicate
的更改将影响it
所证明的那样。在第五次和第六次调用时,更改将传播回 v2,因此第二次调用将“看到”它。
【讨论】:
人们通常会将此描述为装箱...在第三次和第四次调用中,您正在对方法调用本身进行装箱:当 T 是一个接口时,it
是一个装箱值(有时这种装箱可以虽然被优化掉了),并且重复只是对该框的引用。在第五次和第六次调用中,您将传入已经装箱的对象,因为IValue v2 = v1;
创建了一个盒子。由于您两次传入同一个框而不是创建两个单独的框,因此在第一次调用时对框所做的更改在第二次调用时可见。
@AnorZaken:“装箱”这个词确实是用来描述这个过程的。我没有方便的 .NET 内部文档,但它确实描述了具有两种不同类型的过程,并且我认为认识到盒装结构是 Object
而未装箱结构不是比 C# 中使用的抽象模型更清晰。 VB.NET 在其中添加了一些自己的愚蠢逻辑。如果接口类型引用标识了一个装箱值类型实例,则将引用转换为类型Object
将重新装箱该实例,原因我不太了解。【参考方案4】:
static class Metadata<T>
static public readonly Type Type = typeof(T);
static public readonly bool IsValueType = Metadata<T>.Type.IsValueType;
//fast test if T is ValueType
if(Metadata<T>.IsValueType) //only read static readonly field!
//...
【讨论】:
这个限制是它基于typeof(T)
而不是测试一个传入的实例。通常程序员知道一个特定的type
是否是值,通常需要知道一个instance
是否是值。 考虑一个方法参数object obj
。这个答案将基于T=object
,声明的参数类型,而不是特定实例的运行时类型来评估,所以无论obj
是什么,都会返回false
。但是obj
可能是一个盒装整数或其他值类型。【参考方案5】:
有两条规则:
1-所有类都是reference类型,如Object和String,所以.NET Framework支持classes。
2-所有结构都是value类型,例如bool和char,即使它包含引用成员,所以它被.NET Framework structures支持。
只需右键单击任何类型,如果是 Class 则 Go To Definition 表示它是引用类型,否则如果它是 Struct 则表示它是值类型 :)
【讨论】:
++ 作为背景信息,但是“即使它包含引用成员”是什么意思?另外,我假设 GUI 说明是指 Visual Studio,对吗? 对,它指的是 Visual Studio,我的意思是你可以让 struct 包含一个对象引用......你可以在 msdn.microsoft.com/en-us/library/t63sy5hs.aspx 上找到这两个句子“类是一个引用类型。对于因此,.NET Framework 类支持 Object 和 String 等引用类型。请注意,每个数组都是引用类型,即使它的成员是值类型。 “结构是一种值类型,即使它包含引用类型成员。因此,Char 和 Integer 等值类型是由 .NET Framework 结构实现的。” 很有趣,但问题是关于代码,而不是在 IDE 中查找。 问题是关于泛型类型 T,记得吗? “转到定义”对 T 没有帮助。【参考方案6】:你可以使用
obj.GetType().IsValueType
这使用反射但清晰的方式而不是装箱拆箱。
【讨论】:
那个回复没有回答问题。以上是关于检查对象是不是为值类型的最有效方法的主要内容,如果未能解决你的问题,请参考以下文章
检查未知对象中的对象是不是存在的最有效的Javascript方法[重复]