5

我在lib1.dllClass1.GetChild<T>() where T : DependencyObject程序集中创建了扩展方法。之后,依赖lib1.dll的所有程序集都无法编译并出现错误:

'System.Windows.DependencyObject' 类型在未引用的程序集中定义。您必须添加对程序集“WindowsBase”等的引用...

为什么依赖程序集即使不使用也需要WindowsBaseGetChild

.

重现(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 指出的那样,类型推断可能会发挥一些作用。仍然很想知道来龙去脉。

4

6 回答 6

3

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

还有其他用例独立于错误产生此错误的扩展方法。

考虑一下:

  1. 在类型中定义一个泛型方法,比如 TP1,在库中定义,比如 LB1。
  2. 类型将泛型方法限制在某个其他库 LB2 中定义的某个类型上。
  3. 在 TP1 中定义另一个方法。
  4. 现在在您的库中仅引用 LB1 并尝试调用 TP1 类型的第二种方法

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

于 2012-09-28T14:04:23.567 回答
2

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

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

或者,正如 prashanth 指出的那样,将其SomeExtMethod放入不同的程序集中,这样使用它的代码就不需要依赖于 WindowsBase。

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

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

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

于 2012-09-21T15:46:45.470 回答
0

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

我认为它在对标识符执行比较之前尝试某种形式的推理(这将立即排除扩展方法,因为它的名称错误)。显然,如果它无法理解类型约束,它就无法对该类型执行任何形式的推理。

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

于 2012-09-21T14:01:44.750 回答
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到签名中,则确实需要它。

实际上,您可以在项目中使用您想要的任何程序集引用,只要您不以任何方式公开它们。一旦你公开了它们,那么使用你的库的每个其他项目都需要能够处理它们,因此需要这些引用本身。

于 2012-09-27T02:53:28.447 回答
0

也许 ILMerge 会解决这个问题。这个想法是您创建 2 个 dll 并将它们合并为一个。这样你就可以有一个 dll 但引用它两次。然后,您可以将 GUI 代码与其他代码分开,只将您需要的引用添加到特定项目中。

于 2012-09-27T21:19:33.217 回答
0

答案很简单。这是因为该方法是公开的。这意味着lib2.dll(在您的情况下)它是可见的。换句话说,您可以调用此方法。

它还有一个约束,只有继承自的类DependencyObject才能调用此方法。这就是您需要引用“WindowsBase”的原因。

于 2012-09-28T09:10:57.290 回答