我刚刚修改了深度C#的第4章,它处理了可空类型,我正在添加一个关于使用"as"运算符的部分,它允许你编写:
object o = ...;
int? x = o as int?;
if (x.HasValue)
{
    ... // Use x.Value in here
}
我认为这非常简洁,它可以提高性能而不是C#1等效,使用"is"后跟一个演员 - 毕竟,这样我们只需要请求动态类型检查一次,然后进行简单的值检查.
然而,情况似乎并非如此.我在下面包含了一个示例测试应用程序,它基本上对对象数组中的所有整数求和 - 但该数组包含许多空引用和字符串引用以及盒装整数.该基准测试您必须在C#1中使用的代码,使用"as"运算符的代码,以及用于踢LINQ解决方案的代码.令我惊讶的是,在这种情况下,C#1代码的速度提高了20倍 - 即使是LINQ代码(考虑到所涉及的迭代器,我预计它会更慢)也胜过"as"代码.
可以isinst为空的类型的.NET实现真的很慢吗?是unbox.any导致问题的附加因素吗?还有另一种解释吗?目前,我觉得我必须在性能敏感的情况下包含警告,禁止使用它...
结果:
演员:10000000:121
As:10000000:2211
LINQ:10000000:2143
码:
using System;
using System.Diagnostics;
using System.Linq;
class Test
{
    const int Size = 30000000;
    static void Main()
    {
        object[] values = new object[Size];
        for (int i = 0; i < Size - 2; i += 3)
        {
            values[i] = null;
            values[i+1] …我正在寻找一个清晰,简洁和准确的答案.
理想情况下,作为实际答案,虽然欢迎链接到良好的解释.
我注意到Java SE 6和Java SE 7之间的自动拆箱行为有所不同.我想知道为什么会这样,因为我找不到这两个版本之间这种行为的变化的任何文档.
这是一个简单的例子:
Object[] objs = new Object[2];
objs[0] = new Integer(5);
int myInt = (int)objs[0];
这与Java SE 7中的javac编译良好.但是,如果我给编译器"-source 1.6"参数,我在最后一行得到一个错误:
inconvertible types
found   : java.lang.Object
required: int
我尝试下载Java SE 6以使用本机版本6编译器进行编译(不带任何-source选项).它同意并给出与上面相同的错误.
什么给出了什么?从一些更多的实验看来,Java 6中的拆箱似乎只能清楚地(在编译时)是盒装类型的unbox值.例如,这适用于两个版本:
Integer[] objs = new Integer[2];
objs[0] = new Integer(5);
int myInt = (int)objs[0];
因此,似乎在Java 6和7之间,取消装箱功能得到了增强,因此它可以一次性抛出和取消对象类型,而不知道(在编译时)该值是正确的盒装类型.但是,通过阅读Java语言规范或Java 7发布时写的博客文章,我看不出有什么变化,所以我想知道改变是什么以及这个"功能"被称为什么?
好奇心:由于变化,可能会触发"错误"的拆箱:
Object[] objs = new Float[2];
objs[0] = new Float(5);
int myInt = (int)objs[0];
这编译得很好但在运行时给出了ClassCastException.
对此有何提及?
.NET 1.0创建整数集合的方法(例如)是:
ArrayList list = new ArrayList();
list.Add(i);          /* boxing   */
int j = (int)list[0]; /* unboxing */
使用它的代价是由于装箱和拆箱而缺乏类型安全性和性能.
.NET 2.0方式是使用泛型:
List<int> list = new List<int>();
list.Add(i);
int j = list[0];
拳击的价格(据我所知)是需要在堆上创建一个对象,将堆栈分配的整数复制到新对象,反之亦然,以便取消装箱.
泛型的使用如何克服这个问题?堆栈分配的整数是否保留在堆栈上并从堆中指向(我想这不是这种情况,因为当它超出范围时会发生什么)?似乎仍然需要将其复制到堆栈外的其他地方.
真的发生了什么?
我有一个IDataRecord reader我正在检索小数,如下所示:
decimal d = (decimal)reader[0];
由于某种原因,这会抛出一个无效的强制转换异常,说"指定的强制转换无效".
当我这样做reader[0].GetType()时告诉我它是Int32.据我所知,这应该不是问题....
我已经通过这个片段测试了这个,它运行得很好.
int i = 3750;
decimal d = (decimal)i;
这让我头疼不已,想知道为什么它没有将读取器中包含的int取消装箱作为小数.
有谁知道为什么会这样?有什么微妙的我不见了吗?
请考虑以下代码
public class JDK10Test {
    public static void main(String[] args) {
        Double d = false ? 1.0 : new HashMap<String, Double>().get("1");
        System.out.println(d);
    }
}
在JDK8上运行时,此代码会打印,null而在JDK10上会生成此代码NullPointerException
Exception in thread "main" java.lang.NullPointerException
    at JDK10Test.main(JDK10Test.java:5)
除了JDK10编译器生成的两个与自动装箱相关的附加指令外,编译器生成的字节码几乎完全相同,并且似乎负责NPE.
15: invokevirtual #7                  // Method java/lang/Double.doubleValue:()D
18: invokestatic  #8                  // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
这种行为是JDK10中的错误还是故意更改以使行为更严格?
JDK8:  java version "1.8.0_172"
JDK10: java version "10.0.1" 2018-04-17
是否有一个用于自动区分的Haskell库,可以使用未装箱的向量?在grad从功能Numeric.AD要求的一个实例Traversable,这Data.Vector.Unboxed是没有的.
编辑:底部的评论.还有,这个.
这就是让我感到困惑的一点.我的理解是,如果我有这样的枚举......
enum Animal
{
    Dog,
    Cat
}
...我基本上完成的是定义了一个用两个定义的值调用的值类型Animal,Dog和Cat.这种类型派生自引用类型 System.Enum(值类型通常不能执行的操作 - 至少不在C#中 - 但在这种情况下是允许的),并且具有用于在int值之间来回转换的工具.
如果我刚刚描述上面的枚举类型的方式是真的,那么我希望以下代码抛出InvalidCastException:
public class Program
{
    public static void Main(string[] args)
    {
        // Box it.
        object animal = Animal.Dog;
        // Unbox it. How are these both successful?
        int i = (int)animal;
        Enum e = (Enum)animal;
        // Prints "0".
        Console.WriteLine(i);
        // Prints "Dog".
        Console.WriteLine(e);
    }
}
通常,您不能将值类型System.Object从其确切类型以外的任何其他方式取消装箱.那以上怎么可能呢?这是因为如果该 …
今天我偶然发现了一个有趣的错误.我有一组属性可以通过一般的setter设置.这些属性可以是值类型或引用类型.
public void SetValue( TEnum property, object value )
{
    if ( _properties[ property ] != value )
    {
        // Only come here when the new value is different.
    }
}
在为此方法编写单元测试时,我发现值类型的条件始终为真.我花了很长时间才弄清楚这是由于拳击/拆箱.我没有花太多时间将代码调整为以下内容:
public void SetValue( TEnum property, object value )
{
    if ( !_properties[ property ].Equals( value ) )
    {
        // Only come here when the new value is different.
    }
}
问题是我对这个解决方案并不完全满意.我想保留一个简单的参考比较,除非该值被加框.
我想到的当前解决方案只是要求Equals()盒装值.这样做的装箱值的检查似乎有点矫枉过正.是不是有更简单的方法?