我在 lib1.dll 程序集中创建了Class1.GetChild<T>() where T : DependencyObject
扩展名方法。之后,依赖于 lib1.dll 的所有程序集都无法编译并出现错误:
类型'System.Windows.DependencyObject'是以可装配的方式定义的 没有引用。您必须添加对程序集的引用 'WindowsBase'等......
为什么依赖程序集需要 WindowsBase ,即使它们不使用GetChild
?
。
要重现(vs2010 .net4):
lib1.dll (引用 WindowsBase )
namespace lib1
{
public static class Class1
{
public static T GetChild<T>(this DependencyObject src) where T : DependencyObject
{
return default(T);
}
}
public static class Class2
{
public static int SomeExtMethod(this string src)
{
return 0;
}
}
}
lib2.dll (引用 lib1 但不是 WindowsBase )
using lib1;
class someClass
{
void someFct()
{
"foo".SomeExtMethod(); // error: The type 'System.Windows.DependencyObject'
// is defined in an assemebly that is not referenced.
// You must add a reference to assembly 'WindowsBase' etc..
}
}
的更新 的
我认为在混合泛型方法和扩展方法时确实存在一些问题。我试图在以下示例中演示该问题:
// lib0.dll
namespace lib0
{
public class Class0 { }
}
// lib1.dll
using lib0;
namespace lib1
{
public static class Class1
{
public static void methodA<T>() where T : Class0 { } // A
public static void methodB(Class0 e) { } // B
public static void methodC(this int src) { } // C
}
public static class Class2
{
public static void methodD(this String s) { }
}
}
// lib2.dll
using lib1;
class someClass
{
void someFct()
{
Class2.methodD(""); // always compile successfully
"".methodD(); // raise the 'must add reference to lib0' error depending on config. see details below.
}
}
A, //B, //C
- &gt;编译确定
A, B, //C
- &gt;编译确定
//A, B, C
- &gt;编译确定
A, //B, C
- &gt;提出错误
A, B, C
- &gt;提出错误
//A
表示methodA
已发表评论。正如Damien指出的那样,类型推断可能会发挥一些作用。仍然很想知道这些来龙去脉。
答案 0 :(得分:3)
微软已经回答了你的情况: https://connect.microsoft.com/VisualStudio/feedback/details/668498/problem-with-extension-method-in-c-compiler
还有其他用例也与扩展方法无关,错误地产生了这种错误。
考虑一下:
如果您不使用TP1但在LB1中定义了其他类型,则不会出现错误。 此外,即使TP1类型的方法之一需要LB2中定义的类型的参数(并且您不调用此方法),它也不会产生此错误
答案 1 :(得分:2)
当一个程序集依赖于另一个程序集时,第一个程序集还依赖于 all 另一个程序集的依赖关系 - 无论使用什么。程序集依赖关系有效地解耦,编译后可以部署任一程序集的另一个版本,编译器无法知道在这种情况下,第二个程序集中的一个或多个依赖项将不会被第一个程序集使用。
要解决此问题,您只需添加对WindowsBase的引用。
或者,正如prashanth指出的那样,将SomeExtMethod
放入另一个程序集中,因此使用它的代码不需要依赖于WindowsBase。
更新: 如果不使用程序集中的任何内容,则不需要任何依赖项。但是,只要使用一个程序集,就需要该程序集的所有依赖项。这在Visual Studio添加引用的方式中很明显。如果添加对程序集的引用,它将所有依赖程序集(未在GAC中注册)与您添加的程序集一起复制到调试/发布目录中。
更新: 关于编译错误:这是它的编写方式 - 可能没有其他原因。如果不引用依赖程序集,那么获取编译错误是一个好主意吗?也许,您可能会使用引用中的某些内容,并且可能直接使用引用引用中的内容 - 更好的编译错误而不是部署错误。
为什么每个未引用的辅助依赖项上都没有编译错误?再次,它是这样编写的。也许这里的错误也会很好;但这将是一个突破性的变化,需要非常有说服力的理由。
答案 2 :(得分:0)
我不确定编译器团队中的任何人都可以回答这个问题。我现在认为它与类型推断有关 - 但是§7.6.5.1方法调用讨论推理,§7.6.5.2扩展方法调用是静默的问题 - 尽管在搜索适用的扩展方法时显然确实发生了推断。
我认为它正在尝试某种形式的推理之前它正在执行标识符的比较(这将立即排除扩展方法,因为它的名称错误)。显然,如果它无法理解类型约束,它就无法对此类型进行任何形式的推断。
因此,当您将类型约束更改为class
时,它现在已成功传递此方法 - 它可以推断出类型参数,但它现在成功地消除了此扩展方法。
答案 3 :(得分:0)
当你引用另一个程序集时,我假设编译器需要能够解析该程序集中定义的任何方法签名,因此它知道在看到该函数的调用时去哪里找到该函数它
如果用
替换GetChild()
功能
public static T GetChild<T>(this T src)
{
if (typeof(T) == typeof(DependencyObject)) return default(T);
else return default(T);
}
或类似的内容,不要求您包含对您正在运行的 WindowsBase 的引用。但是,如果您在签名中添加where T : DependencyObject
,则 需要它。
实际上,只要不以任何方式公开它们,就可以在项目中使用所需的任何程序集引用。一旦你暴露它们,那么使用你的库的每个其他项目都需要能够处理它们,因此需要这些引用。
答案 4 :(得分:0)
也许ILMerge会解决这个问题。我们的想法是创建2个dll并将它们合并为一个。这样你就可以拥有一个dll但引用它两次。然后,您可以将GUI代码与其他代码分开,只添加您需要的特定项目的引用。
答案 5 :(得分:0)
答案很简单。这是因为该方法是十分公开的。这意味着它lib2.dll
可见(在您的情况下)。换句话说,您可以调用此方法。
它还有一个约束条件,即只有从DependencyObject
继承的类才能调用此方法。这就是你需要引用'WindowsBase'的原因。