Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reference required error only when using LINQ

I've got 3 C# projects/assemblies, A, B and C. B references A, C references B.

In A:

using System.Collections.Generic;

namespace A
{
    public class Dict : Dictionary<string, object> {}
    public class Options: Dict {}
}

In B:

using System.Collections.Generic;
using A;

namespace B
{
    public class Client
    {
        public void M(IDictionary<string, string> d)
        {
            var dict = d.ToDict<Options>();
        }
    }

    public static class Extensions
    {
        public static T ToDict<T>(this IDictionary<string, string> dictionary)
           where T : Dict, new()
        {
            return new T();
        }
    }
}

In C:

using System.Collections.Generic;
using B;

namespace C
{
    public class Worker
    {
        public void M()
        {
            var client = new Client();
            var d = new Dictionary<string, string> { { "k", "v" } };
            var strings = new [] { "one", "two" }; // never used
            client.M(d);
        }
    }
}

This all compiles.

If I import the System.Linq namespace in Project C and add the line strings.Select(s => s); (or any LINQ method) before the call to client.M(), I get a compiler error:

The type 'A.Dict' is defined in an assembly that is not referenced. You must add a reference to assembly 'A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

Making the Extensions class internal or moving it to a different namespace prevents the error.

As B is supposed to be wrapping A and hiding its details from C, it's wrong to let the extension method be public. But I don't understand why this is only picked by the compiler when LINQ is brought in.

Does the presence of at least one extension method cause the compiler to behave slightly differently?

like image 453
Graham Clark Avatar asked Oct 16 '15 09:10

Graham Clark


1 Answers

Adding any unresolved method will trigger this error.

I can't remember where I read this, but C# (VS2013) compiler will stop resolving extension method when it find a matching non-extension method. You can verify this by adding following code in project C.

public static class Extensions
{
    public static void M(this Client c, IDictionary<string, string> d)
    {
        return;
    }
}

It doesn't cause any error!

When a method is NOT resolved with non-extension method (for example, add "abc".Foo() to cause a compiler error), now C# compiler starts to scan extension methods. When it scans B.Extensions.ToDict, it doesn't know what Dict is. However, assembly B's metadata tell compiler it from A.Dict, then you see the error.

like image 190
qxg Avatar answered Oct 19 '22 04:10

qxg