Проблема с LINQ - необходимо добавить ссылку на ненужную библиотеку - PullRequest
13 голосов
/ 13 мая 2011

У меня есть следующая проблема.У меня есть решение, которое содержит около 40 проектов.Есть проект A, который ссылается на проект B, который ссылается на проект C. В проекте A нет кода, который бы использовал классы из проекта C. Однако, если я использую какой-либо метод расширения LINQ в любом коде, например:

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

Я получаю ошибку компилятора:

somefile.cs (70,13): ошибка CS0012: тип "XXX" определен в сборке, на которую нет ссылок.Необходимо добавить ссылку на сборку «Имя сборки Project C, версия = 0.0.0.0, Culture = нейтральный, PublicKeyToken = xxx».

Ошибка в строке, использующей метод расширения linq.

Я использую VS2010, .NET 3.5.

Обновление: Это происходит с каждым методом расширения.Я создал класс в том же файле, который выглядит следующим образом:

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

И затем я пишу этот код, и компиляция прерывается с той же ошибкой:

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

Update2: Хорошо, я выяснил, что вызывает ошибку.Но я не знаю почему.Следующий код вызывает ошибку:

using System.Linq;
using B;

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

Но если я удаляю, используя B (я все еще следую за именами из моего описания, что A ссылается на B, B ссылается на C), он компилируется.Это ЕДИНСТВЕННЫЙ код в проекте А. Он компилируется, если я удаляю использование метода расширения или удаляю «используя Б», как я уже говорил.

Обновление 3:

Прежде всего, спасибо за все ваши предложения.Самый маленький пример, который я могу придумать, следующий.Я создал новый проект, csproj выглядит так (ничего не изменилось, только имя проекта C и руководство по проекту C):

<?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 содержит следующий код:

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

Я получаю следующую ошибку компилятора:

D: \ xxx \ Class1.cs (16,13): ошибка CS0012: тип "xxx" определен в сборке, на которую нет ссылок,Вы должны добавить ссылку на сборку 'xxx, Version = 0.0.0.0, Culture = нейтральный, PublicKeyToken = xxx'.

Если я удаляю using C;, он прекрасно компилируется.

Заранее спасибо за помощь.

Ответы [ 3 ]

9 голосов
/ 14 мая 2011

Вот небольшой пример, который воспроизводит проблему. Это вызвано методом расширения в B, который использует типы, определенные в C, в своей сигнатуре. Даже если метод расширения не используется, ошибка вызывается процессом поиска всех методов расширения, доступных через использование.

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
    {
    }
}

Этот тестовый пример выдает эту ошибку:

ошибка CS0012: тип 'ClassLibraryC.ClassC' определен в сборке, на которую нет ссылок. Необходимо добавить ссылку на сборку "ClassLibraryC, версия = 1.0.0.0, культура = нейтральная, PublicKeyToken = null".

Edit:

По моему мнению , это ошибка в компиляторе C #, потому что это очень удивительное поведение для метода, на который вы не ссылались, которое приводит к сбою компиляции вашей программы. У команды компилятора C # может быть другое мнение.

Тем временем, обходной путь должен поместить ваши методы расширения в B, которые используют типы из C в своей подписи, в отдельное пространство имен и добавить использование в это пространство имен только тогда, когда вызывающая сборка имеет ссылку как на B, так и на C. В качестве альтернативы, если количество нарушающих методов расширения невелико и их значение ограничено, вы можете переместить или удалить их. Наконец, и, что наиболее очевидно, вы можете покончить с этим и просто добавить ссылку на C, которую он говорит вам, что вы должны добавить.

Второе редактирование:

Предостережение: я только что рассмотрел это, чтобы очистить пример, и он слабее, чем я думал. Он завершается ошибкой только в том случае, если имя вызываемого метода расширения в A точно совпадает с именем метода невостребованного расширения в B. Так, например, если вы переименуете Test в B в NotCalled, это больше не приведет к ошибке, но в исходной проблеме , по-видимому, будет.

Окончательное редактирование:

В сотрудничестве мы сузили необходимые условия для воспроизведения проблемы, описанной в первоначальном вопросе: метод расширения в B с типами из C в его сигнатуре, которые используют ограничение общего типа. Приведенный выше тестовый пример является более узким примером. Конкретные условия явно связаны с конкретной реализацией способа, которым компилятор ищет методы расширения.

Таким образом, ссылки - это функция времени выполнения, а методы расширения - функция времени компиляции. Независимо от того, как генерируется сообщение об ошибке, если метод не вызывается, компилятор является тем, кто запрашивает ссылку во время компиляции, сборка не нуждается в ссылке во время выполнения . Поэтому ошибка не та, что у компилятора нет выбора, кроме emit, это просто неудобная ситуация для компилятора.

2 голосов
/ 14 мая 2011

Я нашел решение.

Вы можете скачать все решение (против 2010) по этой ссылке: https://rapidshare.com/files/4269394110/ExtensionProblem.zip.

Я нашел полный ответ благодаря @Rick Sladkey, ответ которого былпочти завершеноЕдинственное, что приводит к тому, что код всегда не компилируется, - это ограничение на универсальный метод в проекте B, который указан с использованием класса из проекта C.

Хорошо, вот листинг:

ПРОЕКТ A

ClassA.cs

using B;

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

Если вы хотите, чтобы код компилировал первую строку комментария (using B;).

Воткласс, который включает случайный метод расширения в проекте A.

ExtensionsA.cs

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

        }
    }
}

PROJECT B

Extensions.cs - в этом классе есть методс общим ограничением 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 - нам нужен этот класс, чтобы использовать его в методе расширения в проекте B

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

Для меня это выглядит как ошибка в компиляторе, которая слишком стремится проверить, может ли метод расширения в проекте B использоваться в проекте A.

Спасибо за все ваши ответы!Особенно @Rick Sladkey.

0 голосов
/ 14 мая 2011

Наиболее вероятным ответом мне кажется, что есть метод расширения на IEnumerable или IEnumrable<T>, называемый Where в B. Этот метод расширения использует тип из C (в качестве возвращаемого типа? Или ограничение типа?), Который требует от вас ссылки на него.

Что Visual Studio говорит вам Where?

Что касается решения, кажется, что B находится под вашим контролем, поэтому вы должны удалить метод расширения, его действительно не должно быть. В качестве альтернативного решения вы можете использовать длинный синтаксис: Enumerable.Where(collection, s => s != null).

...