使用LINQ时出现“需要引用”错误

9
我有3个C#项目/程序集,A、B和C。 B引用A,C引用B。
在A中:
using System.Collections.Generic;

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

在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();
        }
    }
}

在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);
        }
    }
}

这全部编译成功。

如果我在项目C中导入System.Linq命名空间,并在调用client.M()之前添加行strings.Select(s => s);(或任何LINQ方法),我会得到编译器错误:

类型“A.Dict”在未被引用的程序集中定义。 您必须添加对程序集“A,Version=1.0.0.0, Culture=neutral,PublicKeyToken=null”的引用。

Extensions类设置为internal或将其移动到不同的命名空间可以避免此错误。

由于B应该包装A并隐藏其细节,因此让扩展方法成为公共的是错误的。但是我不明白为什么只有在引入LINQ时编译器才会选择这种方式。

是否至少存在一个扩展方法会导致编译器的行为略有不同?


1
无法在VS2015中重现,使用目标框架从3.5到4.6。 - SWeko
@SWeko:我曾经遇到过几种情况,当解决依赖关系时Roslyn更加智能。事实上,同一个项目在使用VS2015编译成功的情况下,如果使用VS2013或早期版本进行编译,则会失败并需要额外的程序集引用。 - Dennis
@SWeko 谢谢,我忘了提到我正在使用VS2013。我已经添加了一个标签。 - Graham Clark
是的,在VS2013上复现了它。 - SWeko
1个回答

2

添加任何未解决的方法都会触发此错误。

我记不清在哪里看到的,但是C#(VS2013)编译器将在找到匹配的非扩展方法时停止解析扩展方法。您可以通过在项目C中添加以下代码来验证此操作。

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

不会引起任何错误!

当使用非扩展方法解析方法时(例如,添加"abc".Foo()以导致编译器错误),C#编译器现在开始扫描扩展方法。当它扫描B.Extensions.ToDict时,它不知道Dict是什么。但是,B程序集的元数据告诉编译器它来自A.Dict,然后你就会看到错误。


好的,谢谢。编译器对于完全不同名称的方法进行深入检查似乎有些奇怪,但也许它会建立一个缓存来存储所有可以找到的扩展方法或类似的东西。 - Graham Clark

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接