C# 混合泛型方法和扩展方法

C# 混合泛型方法和扩展方法,c#,.net,generics,extension-methods,C#,.net,Generics,Extension Methods,我在lib1.dll程序集中创建了Class1.GetChild(),其中T:DependencyObject扩展方法。之后,所有依赖lib1.dll的程序集都无法编译,错误为: 类型“System.Windows.DependencyObject”是在Assembly中定义的 这是没有参考的。必须添加对程序集的引用 “WindowsBase”等 为什么依赖程序集即使不使用GetChild,也需要WindowsBase 要复制(vs2010.net4): lib1.dll(参考WindowsB

我在lib1.dll程序集中创建了
Class1.GetChild(),其中T:DependencyObject
扩展方法。之后,所有依赖lib1.dll的程序集都无法编译,错误为:

类型“System.Windows.DependencyObject”是在Assembly中定义的 这是没有参考的。必须添加对程序集的引用 “WindowsBase”等

为什么依赖程序集即使不使用
GetChild
,也需要WindowsBase

要复制(vs2010.net4):

lib1.dll(参考WindowsBase)

更新:

我认为当混合使用泛型方法和扩展方法时,肯定有一些东西。我试图在以下示例中演示该问题:

// 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.
    }
}
//lib0.dll
命名空间lib0
{
公共类0{}
}
//lib1.dll
使用lib0;
名称空间lib1
{
公共静态类1
{
公共静态void methodA(),其中T:Class0{}//A
公共静态void方法B(class0e){}//B
公共静态void方法C(this int src){}//C
}
公共静态类2
{
公共静态void methodD(此字符串为s){}
}
}
//lib2.dll
使用lib1;
上课
{
void someFct()
{
Class2.methodD(“”;//始终成功编译
“”.methodD();//根据配置引发“必须添加对lib0的引用”错误。请参阅下面的详细信息。
}
}
A、//B、//C
->编译正常

A,B,//C
->编译正常

//A、B、C
->编译正常

A、/B、C
->引发错误

A、B、C
->引发错误


//A
表示注释了
方法A
。正如Damien指出的,类型推断可能起到一定的作用。仍然很想知道细节。

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

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


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

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

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

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

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

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


为什么不在每个未引用的辅助依赖项上出现编译错误?同样,它是这样写的。也许这里的错误也是好的;但是,这将是一个突破性的更改,需要真正有说服力的理由。

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

如果将
GetChild()
函数替换为

    public static T GetChild<T>(this T src)
    {
        if (typeof(T) == typeof(DependencyObject)) return default(T);
        else return default(T);
    }
公共静态T GetChild(此T src)
{
if(typeof(T)=typeof(DependencyObject))返回默认值(T);
否则返回默认值(T);
}
或者类似的,它确实要求您包含对正在运行的WindowsBase的引用。但是如果您将
其中T:DependencyObject
添加到签名中,它确实需要它


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

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

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

它还有一个约束条件
// 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.
    }
}
    public static T GetChild<T>(this T src)
    {
        if (typeof(T) == typeof(DependencyObject)) return default(T);
        else return default(T);
    }