Проблема с LINQ - необходимо добавить ссылку на ненужную библиотеку
У меня есть следующая проблема. У меня есть решение, содержащее около 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 assembly name, Version = 0.0.0.0, Culture = neutral, 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 (я буду следовать за именами из моего описания, которые ссылаются на ссылки B, B C).
Это ТОЛЬКО код в проекте A. Он компилируется, если я удаляю использование метода расширения или если я удаляю "использование B", как я сказал ранее.
Update3:
Прежде всего, спасибо за все ваши предложения.
Самый маленький пример, который я могу придумать, - это следующее. Я создал новый проект, 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>
Класс1.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 = neutral, PublicKeyToken = xxx'.
Если я удалю using C;
, он просто компилируется.
Заранее благодарим за помощь.
Ответы
Ответ 1
Я нашел решение.
Вы можете скачать все решение (vs 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)
{
}
}
}
ПРОЕКТ 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;
}
}
}
ПРОЕКТ C
ClassC.cs - нам нужен этот класс, чтобы использовать его в методе расширения в проекте B
namespace C
{
public class ClassC
{
public string Foo;
}
}
Для меня это похоже на ошибку в компиляторе, которая слишком хочет проверить, может ли метод расширения в проекте B использоваться в проекте A.
Спасибо за все ваши ответы! Особенно @Rick Sladkey.
Ответ 2
Вот небольшой пример, который воспроизводит проблему. Это вызвано методом расширения в B, который использует типы, определенные в C в своей сигнатуре. Несмотря на то, что метод расширения не используется, ошибка запускается процессом поиска всех методов расширения, доступных через 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
{
}
}
Этот тестовый пример вызывает эту ошибку:
error CS0012: Тип "ClassLibraryC.ClassC" определен в сборке, на которую не ссылаются. Вы должны добавить ссылку на сборку ClassLibraryC, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null.
Edit:
В моем мнении, это ошибка в компиляторе С#, потому что это очень удивительное поведение для метода, который вы не ссылались, чтобы ваша программа не скомпилировалась. Команда компилятора С# может иметь другое мнение.
В то же время обходным путем является размещение ваших методов расширения в B, которые используют типы из C в своей сигнатуре в отдельное пространство имен и добавляют использование в это пространство имен только тогда, когда вызывающая сборка имеет ссылку как на B, так и на C. В качестве альтернативы, если количество способов устранения неполадок невелико и их значение ограничено, вы можете их перенести или удалить. Наконец, и, что наиболее очевидно, вы можете сделать это и просто добавить ссылку на C, в которой он сообщает вам, что вы должны добавить.
Второе редактирование:
Предостережение. Я просто просмотрел это, чтобы очистить пример, и он слабее, чем я думал. Он терпит неудачу только в том случае, если имя вызываемого метода расширения в точно совпадает с именем метода невостребованного расширения в B. Так, например, если вы переименовали Test
в B в NotCalled
, он больше не терпит неудачу, но в исходной задаче, предположительно это было бы.
Окончательное редактирование:
В сотрудничестве мы сузили необходимые условия для воспроизведения проблемы, описанной в исходном вопросе: метод расширения в B с типами из C в своей сигнатуре, которые используют ограничение общего типа. Вышеприведенный тестовый пример является более узким примером. Конкретные условия явно связаны с конкретной реализацией способа поиска компилятором методов расширения.
В целом, ссылки - это функция времени выполнения, а методы расширения - функция времени компиляции. Независимо от того, как генерируется сообщение об ошибке, если этот метод не вызывается, компилятор - тот, кто желает ссылки во время компиляции, сборке не требуется ссылка во время выполнения. Поэтому ошибка не в том, что у компилятора нет выбора, кроме как на испускание, это просто неудобная ситуация для компилятора.
Ответ 3
Самый вероятный ответ, по-моему, заключается в том, что существует метод расширения на IEnumerable
или IEnumrable<T>
, называемый Where
в B
. Этот метод расширения использует тип из C
(как тип возвращаемого значения? Или ограничение типа?), Который требует, чтобы вы ссылались на него.
Что говорит Visual Studio Where
?
Что касается решения, кажется, что B
находится под вашим контролем, поэтому вам следует удалить метод расширения, его действительно не должно быть. В качестве альтернативного решения вы можете использовать длинный синтаксис: Enumerable.Where(collection, s => s != null)
.