检查对象是不是为值类型的最有效方法

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&lt;T&gt; 结构返回 false。 您对方法 2 的编辑仍然无效。 obj != null || 将为非空引用类型的对象返回 true。 由于我过于挑剔,呃,我的意思是有帮助,方法 1 不喜欢空的 Nullable&lt;T&gt; 对象。 int? bar = null; 将其传递给函数,你会得到错误的。 (说实话,没想到会这样。) 方法 2 最新编辑。 return obj == null ? false : ... 仍然给 Nullable&lt;T&gt; 带来问题。 【参考方案1】:

您并没有真正测试一个对象——您想要测试类型。要调用它们,调用者必须知道类型,但是……嗯。给定签名&lt;T&gt;(T obj) 唯一理智的答案是:

public bool IsValueType<T>() 
    return typeof(T).IsValueType;

或者如果我们想使用示例对象进行类型推断:

public bool IsValueType<T>(T obj) 
    return typeof(T).IsValueType;

这不需要拳击(GetType() 是拳击),并且Nullable&lt;T&gt; 没有问题。一个更有趣的例子是当你传递object...

 public bool IsValueType(object obj);

在这里,null 已经存在大量问题,因为它可能是一个空的 Nullable&lt;T&gt;(一个结构)或一个类。但一个合理的尝试是:

public bool IsValueType(object obj) 
    return obj != null && obj.GetType().IsValueType;

但请注意,空Nullable&lt;T&gt;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&lt;T&gt; 似乎有问题。我不是 IL 大师,但我相信这涉及到拳击,这在这种情况下效果不佳。 你怎么知道is是最简单的检查?例如,您可以测试一个对象是否用它实现了一个接口,这不是那么“简单”。不知道编译器如何处理它,但请查看 IsAssignableFrom 和 ImplementInterface (由它调用)是如何实现的。您是否了解更多,或者您只是认为is 更快,因为它看起来更简单?【参考方案3】:

定义结构实际上定义了两种类型:值类型和派生自System.ValueType 的类类型。如果请求创建派生自 System.ValueType 的类型的变量、参数、字段或数组(统称为“存储位置”),则系统将改为创建一个存储位置来存储对象的字段,而不是存储对出现这些字段的对象的引用。另一方面,如果请求创建派生自 System.ValueType 的类型的实例,系统将创建派生自 System.ValueType 的类的对象实例。

这可以通过创建一个实现 IValue 的结构来证明:

接口 IValue int 值 get;放;; 结构值结构:IValue 公共 int 值 get;放;;

使用通用测试例程和代码来包装它:

static void Test(T it) where T:IValue T 重复 = 它; it.value += 1; 重复值 += 10; Console.WriteLine(it.value.ToString()); 静态无效测试() ValueStruct v1 = 新的 ValueStruct(); v1.value = 9; IValue v2 = v1; 测试(v1); 测试(v1); 测试(v1); 测试(v1); 测试(v2); 测试(v2);

请注意,在任何情况下,对传递给 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方法[重复]

java参数传递

检查给定对象(引用或值类型)是不是等于其默认值

如何检查对象是不是是Java中的集合类型?

如何检查给定的“方法”对象是不是接受 Julia 0.6 中给定的类型的“元组”?

C#装箱和拆箱