混合泛型方法和扩展方法

时间:2012-09-19 10:08:53

标签: c# .net generics extension-methods

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

6 个答案:

答案 0 :(得分:3)

微软已经回答了你的情况: 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类型的第二种方法
  5. 如果您不使用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'的原因。