混合generics方法和扩展方法

我在Class1.GetChild() where T : DependencyObject创建了Class1.GetChild() 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(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.. } } 

更新:

我认为在混合generics方法和扩展方法时肯定会有一些东西。 我尝试在以下示例中演示此问题:

 // lib0.dll namespace lib0 { public class Class0 { } } // lib1.dll using lib0; namespace lib1 { public static class Class1 { public static void methodA() 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表示方法//A被评论。 正如Damien指出的那样,类型推断可能会起到一定的作用。 仍然很想知道这些来龙去脉。

您的情况已由Microsoft在此处解答: https : //connect.microsoft.com/VisualStudio/feedback/details/668498/problem-with-extension-method-in-c-compiler

还有其他用例也与扩展方法无关,错误地产生了这个错误。

考虑一下:

  1. 在类型中定义generics方法,比如TP1,在库中定义为LB1。
  2. 在某些其他库LB2中定义的某种类型上键入约束generics方法。
  3. 在TP1中定义另一种方法。
  4. 现在您的库中只引用LB1并尝试调用TP1类型的第二种方法

如果您不使用TP1但在LB1中定义了其他类型,则不会出现错误。 此外,即使TP1类型的方法之一需要LB2中定义的类型的参数(并且您不调用此方法),它也不会产生此错误

当一个程序集依赖于另一个程序集时,第一个程序集也依赖于另一个程序集的所有依赖关系 – 无论使用什么。 程序集依赖关系有效地解耦,编译后可以部署任一程序集的另一个版本,编译器无法知道在这种情况下,第二个程序集中的一个或多个依赖项将不会被第一个程序集使用。

要解决此问题,您只需添加对WindowsBase的引用即可。

或者,正如prashanth指出的那样,将SomeExtMethod放入另一个程序SomeExtMethod因此使用它的代码不需要依赖于WindowsBase。

更新:如果您不使用程序集中的任何内容,则不需要任何依赖项。 但是,只要使用一个程序集,就需要该程序集的所有依赖项。 这在Visual Studio添加引用的方式中很明显。 如果添加对程序集的引用,它将所有依赖程序集(未在GAC中注册)与您添加的程序集一起复制到调试/发布目录中。

更新:关于编译错误:这是它的编写方式 – 可能没有其他原因。 如果不引用依赖程序集,那么获取编译错误是个好主意吗? 也许,您可能会使用引用中的某些内容并且可能直接使用引用引用中的内容 – 更好的是编译错误而不是部署错误。

为什么不在每个未引用的辅助依赖项上出现编译错误? 再次,它是这样编写的。 也许这里的错误也会很好; 但这将是一个突破性的变化,需要非常有说服力的理由。

我不确定除了编译器团队的人之外,任何人都可以回答这个问题。 我现在认为它与类型推断有关 – 但是§7.6.5.1方法调用讨论推理, §7.6.5.2扩展方法调用在这个问题上保持沉默 – 尽管在搜索时显然确实发生了推断适用的扩展方法。

我认为它在对标识符进行比较之前会尝试某种forms的推断(这会直接排除扩展方法,因为它的名称错误)。 显然,如果它无法理解类型约束,它就无法对此类型执行任何forms的推断。

因此,当您将类型约束更改为仅仅class ,它现在成功传递此方法 – 它可以推断出类型参数,但它现在成功地消除了此扩展方法。

当你引用另一个程序集时,我假设编译器需要能够解析该程序集中定义的任何方法签名 ,因此它知道在看到该函数的调用时去哪里找到该函数。

如果用。替换GetChild()函数

  public static T GetChild(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是可见的(在你的情况下。)换句话说,你可以调用这个方法。

它还有一个约束,即只有从DependencyObjectinheritance的类才能调用此方法。 这就是你需要引用’WindowsBase’的原因。