假设这是一条 DLL 引用链。
在Tests.dll中,使用以下代码行,其中一切都构建完成。
现在当我将此更改为
我对 Tests.dll 进行构建时出现以下错误:“White.UIItem 未在一个未被引用的程序集中定义。您必须添加对 White.Core.dll 的引用。”但我不想这样做,因为它会破坏我的分层结构。
下面是 Automation.dll 中 result 的类型定义。
为什么在IEnumerable上调用Count()时会导致依赖项泄漏?我怀疑是惰性求值引起的(出于某种原因)- 因此,我在上面的代码行中插入了一个ToArray(),但没有起作用。
更新2011年01月07日:更加好奇了! 如果不添加White.Core引用,则无法构建。 因此,我添加了一个引用并构建它(为了找到难以捉摸的依赖项来源)。 在Reflector中打开它,列出的唯一参考文献是Automation,mscorlib,System.core和NUnit。 因此,编译器放弃了White引用,因为它不需要。 ILDASM还确认没有White AssemblyRef条目。
有什么方法可以彻底解决这个问题吗(主要是出于“现在我想知道为什么”的原因)? 这是VS2010 / MSBuild的错误的可能性有多大?
更新2011年01月07日#2 根据Shimmy的建议,尝试将该方法显式地调用为扩展方法。
这导致了3个修复方案,都可以解决问题。
Tests.dll >> Automation.dll >> White.Core.dll
在Tests.dll中,使用以下代码行,其中一切都构建完成。
result.MissingPaths
现在当我将此更改为
result.MissingPaths.Count()
我对 Tests.dll 进行构建时出现以下错误:“White.UIItem 未在一个未被引用的程序集中定义。您必须添加对 White.Core.dll 的引用。”但我不想这样做,因为它会破坏我的分层结构。
下面是 Automation.dll 中 result 的类型定义。
public class HasResult
{
public HasResult(IEnumerable<string> missingPaths )
{ MissingPaths = missingPaths; }
public IEnumerable<string> MissingPaths { get; set; }
public bool AllExist
{
get { return !MissingPaths.Any(); }
}
}
在调用链中,此构造函数的输入参数是通过以下方式创建的(TreeNode类位于White.Core.dll中)
assetPaths.Where(assetPath => !FindTreeNodeUsingCache(treeHandle, assetPath));
为什么在IEnumerable上调用Count()时会导致依赖项泄漏?我怀疑是惰性求值引起的(出于某种原因)- 因此,我在上面的代码行中插入了一个ToArray(),但没有起作用。
更新2011年01月07日:更加好奇了! 如果不添加White.Core引用,则无法构建。 因此,我添加了一个引用并构建它(为了找到难以捉摸的依赖项来源)。 在Reflector中打开它,列出的唯一参考文献是Automation,mscorlib,System.core和NUnit。 因此,编译器放弃了White引用,因为它不需要。 ILDASM还确认没有White AssemblyRef条目。
有什么方法可以彻底解决这个问题吗(主要是出于“现在我想知道为什么”的原因)? 这是VS2010 / MSBuild的错误的可能性有多大?
更新2011年01月07日#2 根据Shimmy的建议,尝试将该方法显式地调用为扩展方法。
Enumerable.Count(result.MissingPaths)
它停止了抱怨(不确定为什么)。但是在此之后,我移动了一些代码,现在在使用IEnumerable时在不同的位置遇到了相同的问题 - 这次是从磁盘文件中读取和过滤行(与White完全无关)。似乎这是一个“症状修复”。
var lines = File.ReadLines(aFilePath).ToArray();
再次说明,如果我删除ToArray(),它就可以再次编译 - 似乎任何导致可枚举对象被评估的方法(ToArray、Count、ToList等)都会导致此问题。让我试着创建一个可演示此问题的小应用程序...
更新 2011 01 07 #3 哦!更多信息... 原来问题只出现在一个源文件中 - 这个文件害怕LINQ。必须显式调用对Enumerable扩展方法的任何调用。 我进行的重构导致一个新方法被移动到这个源文件中,其中包含一些LINQ :) 仍然不知道为什么这个类不喜欢LINQ。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using G.S.OurAutomation.Constants;
using G.S.OurAutomation.Framework;
using NUnit.Framework;
namespace G.S.AcceptanceTests
{
public abstract class ConfigureThingBase : OurTestFixture
{
....
private static IEnumerable<string> GetExpectedThingsFor(string param)
{
// even this won't compile - although it compiles fine in an adjoining source file in the same assembly
//IEnumerable<string> s = new string[0];
//Console.WriteLine(s.Count());
// this is the line that is now causing a build failure
// var expectedInfo = File.ReadLines(someCsvFilePath))
// .Where(line => !line.StartsWith("REM", StringComparison.InvariantCultureIgnoreCase))
// .Select(line => line.Replace("%PLACEHOLDER%", param))
// .ToArray();
// Unrolling the LINQ above removes the build error
var expectedInfo =
Enumerable.ToArray(
Enumerable.Select(
Enumerable.Where(
File.ReadLines(someCsvFilePath)),
line => !line.StartsWith("REM", StringComparison.InvariantCultureIgnoreCase)),
line => line.Replace("%PLACEHOLDER%", param)));
更新 2011年01月11日 #4
缩小了嫌疑人范围,但找不到动机 :)
周末后继续任务..并使用排除法,最终锁定了有问题的部分。
问题出在Tests.dll源文件中的以下使用指令。
using G.S.OurAutomation.Framework;
接下来,我把矛头对准了这个命名空间中最可能的嫌疑人,并且将WhiteExtensions置于焦点之下。
namespace G.S.OurAutomation.Framework
{
public static class WhiteExtensions
{
public static T PollAndGet<T>(this Window parentWindow, string automationId) where T : UIItem ...
public static Window WaitForWindowWithTitle(this Application application, string windowTitle) ...
public static bool HasTreeNode(this Tree treeHandle, string assetPath) ...
public static HasTreeNodesResult HasTreeNodes(this Tree treeHandle, IEnumerable<string> assetPaths)...
}
}
这导致了3个修复方案,都可以解决问题。
- 将扩展方法转换为普通静态方法。
- 将此类移动到子命名空间
G.S.OurAutomation.Framework.White
中 - 将其设置为内部类(因为它是供内部使用的..再次选择最严格的访问修饰符的指南使我受挫了。)
AllExist
的获取代码并替换为一些通用的东西(如return true;
或return false;
)。我只是想看看是否消除那里的LINQ(扩展方法)可以有所帮助(尽管这可能不会有任何帮助,但我对此一无所知)。 - James