[英]Mixing generic methods and extension methods
我创建了Class1.GetChild<T>() where T : DependencyObject
lib1.dll程序集中的 Class1.GetChild<T>() where T : DependencyObject
扩展方法。 之后,所有依赖lib1.dll的程序集均无法编译并显示以下错误:
类型“ System.Windows.DependencyObject”以未引用的方式定义。 您必须添加对程序集“ WindowsBase”等的引用。
为什么依赖程序集即使不使用GetChild
也需要WindowsBase ?
。
要重现(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
>编译就可以了
A, B, //C
>编译就可以了
//A, B, C
>编译就可以了
A, //B, C
>引发错误
A, B, C
>引发错误
//A
表示对methodA
进行注释。 正如Damien指出的那样,类型推断可能会发挥某些作用。 仍然很想知道来龙去脉。
Microsoft已在此处回答您的情况: https : //connect.microsoft.com/VisualStudio/feedback/details/668498/problem-with-extension-method-in-c-compiler
还有其他一些用例,它们与扩展方法无关,它们会错误地产生此错误。
考虑一下:
如果您不使用TP1,而是使用LB1中定义的其他类型,则不会收到该错误。 同样,即使TP1类型的方法之一期望使用LB2中定义的类型的参数(并且您也未调用此方法),它也不会产生此错误。
当一个程序集依赖于另一个程序集时,第一个程序集也依赖于另一个程序集的所有依赖关系-不管使用什么。 程序集依赖关系有效地分离,编译后可以部署其中一个程序集的另一个版本,编译器无法知道在这种情况下,第一个程序集不会使用第二个程序集中的一个或多个依赖项。
要解决此问题,您只需添加对WindowsBase的引用。
或者,如prashanth所指出的,将SomeExtMethod
放入另一个程序SomeExtMethod
,这样使用的代码不需要依赖WindowsBase。
更新:如果不使用程序集中的任何内容,则不需要任何依赖关系。 但是,一旦使用一个程序集,就也需要该程序集的所有依赖项。 这在Visual Studio添加引用的方式中很明显。 如果添加对程序集的引用,它将把所有相关的程序集(未在GAC中注册)与添加的程序集一起复制到调试/发布目录中。
更新:关于编译错误:这就是它的编写方式-可能没有其他原因。 如果您不引用依赖程序集,则出现编译错误是个好主意吗? 也许,您可能会使用引用中的某些内容,而直接使用引用中的某些内容,这比编译错误胜于部署错误。
为什么不对每个未引用的辅助依赖项进行编译错误? 同样,它是这样写的。 也许这里出现错误也将是一个好习惯。 但这将是一个巨大的变化,并且需要真正有说服力的理由。
我不确定编译器团队中的任何人都可以回答此问题。 我现在认为这与类型推断有关-但是尽管§7.6.5.1方法调用讨论了推断, 但是§7.6.5.2扩展方法调用对此没有任何提示-尽管事实上在搜索时确实发生了推断适用的扩展方法。
我认为在对标识符执行比较之前 ,它正在尝试某种形式的推理(由于名称错误,它会立即排除扩展方法)。 显然,如果无法理解类型约束,它将无法对此类型执行任何形式的推断。
因此,当您将类型约束更改为class
,它现在可以成功地越过此方法-它可以推断类型参数,但是现在可以成功地消除此扩展方法。
当您引用另一个程序集时,我假设编译器需要能够解析该程序集中定义的任何方法签名 ,因此,如果看到该函数的调用,它便知道该去哪里查找该函数。
如果将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
添加到签名中,则确实需要签名。
有效地,您可以使用项目中所需的任何程序集引用,只要您不以任何方式公开它们即可。 公开它们之后,使用您的库的所有其他项目都必须能够处理它们,因此需要这些引用本身。
也许ILMerge可以解决此问题。 这个想法是您创建2个dll并将它们合并为一个。 这样,您可以拥有一个dll,但可以两次引用它。 然后,您可以将GUI代码与其他代码分开,而仅将所需的引用添加到特定项目。
答案很简单。 这是因为该方法不公开。 这意味着它对lib2.dll
是可见的(对于您而言)。换句话说,您可以调用此方法。
它还有一个约束,只有从DependencyObject
继承的类才能调用此方法。 因此,这就是为什么您需要引用“ WindowsBase”的原因。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.