开发者

Problem with LINQ - necessary to add reference to unneeded library

I have a following issue. I have a solution that contains about 40 projects. There is a project A that references project B that references project C. There isn't any code in project A that uses classes from project C. However, if I use any LINQ extension method in any code, e.g.:

var r = new int[] { 1, 2, 3 }.Where(a => a > 1);

I get compiler error:

somefile.cs(70,13): error CS0012: The type 'XXX' is defined in an assembly that is not referenced. You must add a reference to assembly 'Project C assembly name, Version=0.0.0.0, Culture=neutral, PublicKeyToken=xxx'.

The error is on the line that uses linq extension method.

I'm using VS2010, .NET 3.5.

Update: It happens with every extension method. I have created a class in the same file, that looks like this:

public static class SomeClass
{
    public static int[] Test(this int[] a)
    {
        return a;
    }
}

And then I write this code and compilation breaks with same error:

new int[] { 1, 2, 3 }.Test();

Update2: Ok, I found out what causes the error. But I don't know why. Following code causes error:

using System.Linq;
using B;

namespace TestApp
{
    public class A
    {
        public void M()
        {
            var c = new string[] { "a", "b", "c" }.Where(s => s != null);
        }
    }
}

But if I remove using B (I'm stil following the names from my description that A references B, B references C) it compiles. This is the ONLY code in project A. It compiles if I remove usage of extension method or if I remove "using B" as I said before.

Update3:

First of all, thanks for all your suggestions. The smallest example I can come up with is the following. I created a new project, csproj looks like this (nothing has been changed, only C project name and C project guid):

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>8.0.30703</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{B649AB2C-926A-4AD1-B7E3-5A29AE1E9CC2}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>ClassLibraryTest</RootNamespace>
    <AssemblyName>ClassLibraryTest</AssemblyName>
    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\C.csproj">
      <Project>{55AFFA2D-63E0-4BA9-XXXX-B70E6A936F5E}</Project>
      <Name>C</Name>
    </ProjectReference>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Class1.cs" />
  </ItemGroup>
  <ItemGroup>
    <Folder Include="Properties\" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

Class1.cs contains following code:

using C;

namespace TestApp
{
    public static class Ext
    {
        public static void M213dsacxvz(this string[] a)
        {
        }
    }

    public class A
    {
        public void B()
        {
            new string[] { "a", "b", "c" }.M213dsacxvz();
        }
    }
}

The compiler error I get is开发者_StackOverflow社区 as follows:

D:\xxx\Class1.cs(16,13): error CS0012: The type 'xxx' is defined in an assembly that is not referenced. You must add a reference to assembly 'xxx, Version=0.0.0.0, Culture=neutral, PublicKeyToken=xxx'.

If I remove using C; it compiles just fine.

Thanks in advance for help.


Here is a small example that reproduces the problem. It is caused by an extension method in B that uses types defined in C in its signature. Even though the extension method is not used, the error is triggered by the process of searching all extension methods that are accessible via usings.

ConsoleApplicationA.cs:

using ClassLibraryB;

namespace ConsoleApplication1
{
    public static class Extensions
    {
        public static int Test(this int a)
        {
            return a;
        }
    }

    public class ProgramA
    {
        static void Main(string[] args)
        {
            0.Test();
        }
    }
}

ClassLibraryB.cs:

using ClassLibraryC;

namespace ClassLibraryB
{
    public static class Extensions
    {
        public static ClassC Test(this ClassB b)
        {
            return new ClassC();
        }
    }

    public class ClassB
    {
    }
}

ClassLibraryC.cs:

namespace ClassLibraryC
{
    public class ClassC
    {
    }
}

This test case produces this error:

error CS0012: The type 'ClassLibraryC.ClassC' is defined in an assembly that is not referenced. You must add a reference to assembly 'ClassLibraryC, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

Edit:

In my opinion, this is a bug in the C# compiler, because it very surprising behavior for a method you did not reference to cause your program to fail to compile. The C# compiler team may have a different opinion.

In the meantime, a workaround is to place your extension methods in B that use types from C in their signature into a separate namespace and add a using to that namespace only when the calling assembly has a reference to both B and C. Alternatively if the number of offending extension methods is small and their value is limited you might be able to move or remove them. Finally, and most obviously, you can be done with it and simply add the reference to C that it tells you you should add.

Second edit:

Caveat: I just reviewed this to clean up the example and it is weaker than I had thought. It only fails if the name of the called extension method in A exactly matches the name of the uncalled extension method in B. So for example if you rename Test in B to NotCalled it no longer fails, yet in the original problem, presumably it would.

Final edit:

With collaboration we narrowed down the necessary conditions to reproduce the issue described in the original question: an extension method in B with types from C in its signature that use a generic type constraint. The above test case is a narrower example. The specific conditions are clearly related to the specific implementation of the way the compiler searches for extension methods.

In summary, references are a run-time feature and extension methods are a compile-time feature. No matter how the error message is generated, if the method is not called, the compiler is the one who desires the reference at compile-time, the assembly does not need the reference at run-time. Therefore the error is not one that the compiler has no choice but the emit, it's just an inconvenient situation for the compiler.


I found the solution.

You may download the whole solution (vs 2010) from this link: https://rapidshare.com/files/4269394110/ExtensionProblem.zip.

I found the complete answer thanks to @Rick Sladkey whose answer was almost complete. The only thing that causes the code to always fail to compile is a constraint on generic method in project B that is specified using class from project C.

Ok, here is the listing:

PROJECT A

ClassA.cs

using B;

namespace A
{
    public class ClassA
    {
        public void Foo()
        {
            new int[] { 1, 2, 3 }.ExtMethodC();
        }
    }
}

If you want the code to compile comment first line (using B;).

Here is a class that includes random extension method in project A.

ExtensionsA.cs

namespace A
{
    public static class ExtensionsA
    {
        public static void ExtMethodC(this int[] a)
        {

        }
    }
}

PROJECT B

Extensions.cs - this class has method with generic constraint T : ClassC

using C;

namespace B
{
    public static class Extensions
    {
        public static string ExtMethodB<T>(this T cInstance) where T : ClassC
        {
            return cInstance.Foo;
        }
    }
}

PROJECT C

ClassC.cs - we need this class to use it in extension method in project B

namespace C
{
    public class ClassC
    {
        public string Foo;
    }
}

For me it looks like a bug in compiler that is too eager to check if extension method in project B may be used in project A.

Thanks for all your answers! Especially @Rick Sladkey.


The most likely answer seems to me to be that there is an extension method on IEnumerable or IEnumrable<T> called Where in B. This extension method uses a type from C (as return type? or type constraint?) which requires you to reference it.

What does Visual Studio tell you Where is?

As for solution, it seems B is under your control, so you should delete the extension method, it really shouldn't be there. As an alternative solution, you can use the long syntax: Enumerable.Where(collection, s => s != null).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜