سؤال

I'm using the ILNumerics library for a project, but I found that variables declared with "var" will sometimes change value in the middle of computation, for example:

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

The x is valid in the first line, however causes "NullReference" exception after execution of the second line. But If I do:

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

The computation will be fine. So what is the problem here? Does that mean we need to be cautious when using "var"?

هل كانت مفيدة؟

المحلول

It's not a bug - it's a feature :)

The var keyword is not allowed to be used in this context. This is part of a collection of three ILNumerics specific rules. It can also be found in the guickstart guide.

In short, the ILNumerics memory management relies heavily on implicit type conversions. All functions / properties return array types of ILRetArray<T>. These return types are volatile by design: they dispose off their storage after the first use. Always. So one can give them to some other function or query some information from them. But you would rather not try to access it a second time!

However, the only way to access such an object twice would be to have a reference (a regular local variable) around. ILNumerics specifies that all local variables must be of type ILArray, ILLogical or ILCell. Once you assign an array to a variable of one of those types, implicit type conversions kick in and 'turn' the volatile return object into something more stable and safe to access several times.

This is why the var keyword is forbidden in C# with ILNumerics. Similar thing for Visual Basic, where you also must declare the type of local array variables explicitly. I once made a blog post about the issue:

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

نصائح أخرى

When declared with var, x is referring to something of type ILRetArray'1; if declared as ILArray<double>, it's ILArray'1. Those are sibling subclasses of a common base (ILDenseArray). A's type is ILArray<double> and its array-indexing operator returns type ILRetArray<ElementType>.

There are implicit conversion operators going both ways here between those two classes.

It looks like when A[full, 0] is assigned to a variable whose type is left up to type inference, the compiler makes it the return type of A's array indexing operator: ILRetArray<ElementType>. But if you declare x's type explicitly as ILArray<double>, an implicit conversion operator is invoked and you get a different actual object.

Then you pass it to norm, which expects ILInArray, and yet another implicit conversion operator is invoked. And there's a bug in that one. It breaks something in the internal state of ILRetArray. Try this line before the call to norm:

var d = (ILInArray<double>)x;

...and it will have the same effect on x as the call to norm did. So it's that conversion.

I haven't downloaded the source to ILNumerics to identify the details of the bug. It's possible that this is meant to be a forbidden operation, in which case I'd wish they had better error reporting (UPDATE see @HaymoKutschbach's answer below: This is in fact the case; ILRetArray is a volatile type by design, intended to be valid only until the first implicit conversion operator is called on it).

It might be fun to submit a bug report to ILNumerics and see what you hear back (UPDATE: There's a guy from ILN in this thread, so never mind).

But in answer to your question: You do need to be a bit cautious when using var; avoid it when there's any possibility of ambiguity. You need to be especially cautious when implicit conversion operators are involved. I had forgotten those things even existed in C#. I'm not fond of them. Of course the bug wasn't caused by their use of an implicit conversion operator; it's just a bug. But the operators vastly and invisibly confused the issue of what was going on in the code. I prefer type conversions to be visible in the source.

Thanks for posting this, it was fun.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top