Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why am I required to reference System.Numerics with this simple LINQ expression?

Tags:

c#

.net

linq

To start, I know how to reference System.Numerics to give the compiler access to the Complex type it's asking for, I just don't understand why it's necessary.

I have this basic struct:

/// <summary>
///     Describes a single point on a spectrum.
/// </summary>
public struct SpectrumPoint
{
    public SpectrumPoint(double wavelength, double intensity)
    {
        Wavelength = wavelength;
        Intensity = intensity;
    }
    public double Intensity { get; }
    public double Wavelength { get; }
}

It is used in a class that needs double[] arrays to use as arguments to third party dependency. I use this LINQy lambda chain construct them:

using Accord.Math;
// ...
double[] _wavelengths = points.Select(point => point.Wavelength).ToArray();
double[] _intensities = points.Select(point => point.Intensity).ToArray();

This is the error caused by those LINQ expressions:

Error CS0012
The type Complex is defined in an assembly that is not referenced.
You must add a reference to assembly
System.Numerics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

I know this sort of error can be caused by unused method overloads that reference external assemblies, like here, but neither .Select() nor .ToArray() have any overloads that refer to Complex. What's going on?


EDIT:

This exactly replicates the compilation problem, which goes away if using Accord.Math; is removed:

using System.Linq;
using Accord.Math;

public class A
{
    public A(IEnumerable<double> d)
    {
        double[] arr = d.ToArray();
    }
}

(Here is Accord.Math.)

like image 266
kdbanman Avatar asked Sep 03 '15 16:09

kdbanman


1 Answers

I believe the problem is due to Accord.Math.ComplexMatrix - a static class with a bunch of extension methods, including:

public static double[,] ToArray(this Complex[] c);

I get the error with:

using static Accor.Math.ComplexMatrix;

... but I don't get it with a similar using static directive for other types in the same namespace.

Changing your ToArray calls to explicitly Enumerable.ToArray calls like this makes the error go away:

_wavelengths = Enumerable.ToArray(points.Select(point => point.Wavelength));
_intensities = Enumerable.ToArray(points.Select(point => point.Intensity));

... which is a further suggestion that it's ComplexMatrix.ToArray causing the problem.

Basically you don't want to use any extension methods imported. Options:

  • Call Enumerable.ToArray directly as shown above
  • Remove using Accord.Math and fully-qualify any uses of types within that namespace
  • Use a namespace alias, e.g. using AM = Accord.Math and then use that for any uses of types within the namespace, e.g. var p3 = new AM::Point3().
  • Use type aliases for any types you need within the namespace, e.g. using Point3 = Accord.Math.Point3; then you can just use var p3 = new Point3(); as normal.

Now admittedly the compiler could potentially work out that that call was infeasible regardless of how Complex is defined, as there can't possibly be a single user-defined conversion from double[] to Complex[], but spotting that would add complexity into both the language and the compiler.

Here's a really short but complete example that demonstrates it:

using System.Linq;
using Accord.Math; // Comment this out and the error goes away

class Test
{
    static void Main(string[] args)
    {
        args.ToArray();
    }
}    
like image 63
Jon Skeet Avatar answered Sep 23 '22 02:09

Jon Skeet