繁体   English   中英

混合通用方法和扩展方法

[英]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

还有其他一些用例,它们与扩展方法无关,它们会错误地产生此错误。

考虑一下:

  1. 在类型为TP1的类库LB1中定义一个通用方法。
  2. 类型将泛型方法限制在其他库LB2中定义的某种类型上。
  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扩展方法调用对此没有任何提示-尽管事实上在搜索时确实发生了推断适用的扩展方法。

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

因此,当您将类型约束更改为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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM