2

我正在为一个项目使用 ILNumerics 库,但我发现用“var”声明的变量有时会在计算过程中改变值,例如:

// A is a matrix
var x = A[full, 0];
double xNorm = (double)norm(x);

x 在第一行中有效,但在执行第二行后会导致“NullReference”异常。但如果我这样做:

// A is a matrix
ILArray<double> x = A[full, 0];
double xNorm = (double)norm(x);

计算会很好。那么这里的问题是什么?这是否意味着我们在使用“var”时需要谨慎?

4

2 回答 2

2

这不是一个错误,这是一个功能 :)

var不允许在此上下文中使用关键字。这是三个 ILNumerics 特定规则集合的一部分。它也可以在guickstart 指南中找到。

简而言之,ILNumerics 内存管理很大程度上依赖于隐式类型转换。所有函数/属性都返回数组类型ILRetArray<T>。这些返回类型在设计上是易失的:它们在第一次使用后丢弃它们的存储。总是。因此,可以将它们提供给其他功能或从中查询一些信息。但是您宁愿不要尝试第二次访问它!

然而,两次访问这样一个对象的唯一方法是在周围有一个引用(一个常规的局部变量)。ILNumerics 指定所有局部变量必须是 ILArray、ILLogical 或 ILCell 类型。一旦将数组分配给其中一种类型的变量,隐式类型转换就会启动并将易失性返回对象“转换”为更稳定和安全的对象,以便多次访问。

这就是为什么var使用 ILNumerics 在 C# 中禁止使用该关键字的原因。对于 Visual Basic,您也必须显式声明局部数组变量的类型。我曾经就这个问题发表过一篇博文:

http://ilnumerics.net/blog/why-the-var-keyword-is-not-allowed-in-ilnumerics/

于 2014-04-07T07:12:41.213 回答
1

当用 var 声明时,x 指的是类型的东西ILRetArray'1;如果声明为ILArray<double>,则为ILArray'1。这些是公共基础 (ILDenseArray) 的兄弟子类。A 的类型是ILArray<double>,它的数组索引运算符返回类型ILRetArray<ElementType>

在这两个类之间有两种方式的隐式转换运算符。

看起来当A[full, 0]被分配给一个类型由类型推断决定的变量时,编译器使它成为 A 的数组索引运算符的返回类型:ILRetArray<ElementType>。但是,如果您将 x 的类型显式声明为ILArray<double>,则会调用隐式转换运算符并且您会得到一个不同的实际对象。

然后将它传递给norm期望ILInArray的 ,并调用另一个隐式转换运算符。那个有一个错误。它破坏了 ILRetArray 的内部状态。在调用之前尝试此行norm

var d = (ILInArray<double>)x;

...并且它对 x 的影响与调用相同norm。所以就是这样的转换。

我还没有将源代码下载到 ILNumerics 以识别错误的详细信息。这可能一个被禁止的操作,在这种情况下,我希望他们有更好的错误报告(更新见@HaymoKutschbach 的回答如下:事实上就是这种情况;ILRetArray设计上是一个 volatile 类型,旨在有效直到第一个隐式转换运算符被调用)。

向 ILNumerics 提交错误报告并查看您收到的回复可能会很有趣(更新:这个线程中有一个来自 ILN 的人,所以没关系)。

但是在回答您的问题时:使用 var; 时确实需要谨慎一点;当有任何歧义的可能性时避免它。当涉及隐式转换运算符时,您需要特别小心。我已经忘记了那些甚至存在于 C# 中的东西。我不喜欢他们。当然,这个错误不是由他们使用隐式转换运算符引起的;这只是一个错误。但是操作员在不知不觉中极大地混淆了代码中发生了什么的问题。我更喜欢类型转换在源代码中可见。

感谢您发布此信息,这很有趣。

于 2014-04-07T04:31:51.150 回答