繁体   English   中英

C#中的通用扩展方法:在这种边缘情况下会发生什么?

[英]Generic extension methods in C#: what will happen in this edge case?

在我最近的一个问题中,我了解到如果有多个扩展方法具有与给定类型匹配的约束,则将选择最具体的扩展方法。 这让我思考 - 编译器如何确定哪一个“更具体”? 结果会是什么?

假设我有以下课程:

public MyClass : IComparable, IDisposable
{
    // Implementation of members
}

public static class MyExtensions
{
    public static void DoSomething<T>(this T item)
        where T : IComparable
    { /* whatever */ }

    public static void DoSomething<T>(this T item)
        where T : IDisposable
    { /* whatever else */ }
}

如果我现在使用扩展方法

var instance = new MyClass();
instance.DoSomething();

将使用哪种方法? 或者编译器会抛出错误?

注意:我不是说这是好设计,甚至我说我需要这样做。 但是“更具体”这个词足以让我思考这一点,现在我必须知道! :P

更新:我想我是不是真的在上面的例子中会发生什么 ,在为什么感兴趣。 因为我做过类似的事情,所以我想到了

public static class CollectionExtensions
{
    public static void DoSomething<T>(this T items) where T : IList { ... }
    public static void DoSomething<T>(this T items) where T : IEnumerable { ... }
}

编译器知道为new List<Something>().DoSomething()选择第一种方法,因为它与传递的类型“更接近”。 我当时感兴趣的是“在这种情况下更接近的意思是什么?如果约束来自两个不同的继承链,编译器将如何反应?为什么?”

在这种情况下,扩展类不会编译 - 您不能仅基于泛型约束重载方法。

如果你把两个扩展方法放到不同的类中,那么调用代码就不会编译 - 这将是一个模糊的调用,因为这两种方法都不会比另一种方法“更好”...在这两种情况下泛型类型的参数都是推断为MyClass ,所以只有两次从MyClassMyClass转换,这两种转换都不比另一种好。

一旦您发现没有适用的实例方法,这基本上只是一个特殊的重载情况。 我最近刚写了一篇关于重载文章 ,你可能觉得它很有用 - 它没有提到这个具体的情况,但是如果你想详细看一下,它就指向规范的相关部分。

它根本不会编译并抛出编译时错误,说两个方法之间的调用是不明确的。

类型'MyExtensions'已经定义了一个名为'DoSomething'的成员,它具有相同的参数类型。

编辑

这就是编译器给出这样的错误的原因。 扩展方法只是语法糖,它们所做的就是为任何类型带来流畅性和可读性。

检查此代码..

var instance = new MyClass();
instance.DoSomething();

编译器将此代码替换为以下内容。

var instance = new MyClass();
MyExtensions.DoSomething(instance);
//Compiler gets confused. Which one to call IComparable or IDisposable

在你的情况下编译器会混淆,因为方法调用有两个匹配的签名,它会给你所说的错误。

通用约束不被视为方法签名的一部分。 编译器将这两种方法视为具有相同签名的方法。 所以你会得到编译错误,说已经定义了DoSomething方法。

public static void DoSomething<T>(this T item)
    where T : IComparable
{ /* whatever */ }

public static void DoSomething<T>(this T item)
    where T : IDisposable
{ /* whatever else */ }

请考虑以下示例:

class MyClass {}

static class MyClassExtensions
{
    public static void DoSomething<T>(this T item, List<string> someList)
    {
        Console.WriteLine("Method with List in signature is called.");  
    }

    public static void DoSomething<T>(this T item, IEnumerable<string> someEnumerable)
    {
        Console.WriteLine("Method with IEnumerable in signature is called.");   
    }
}

在此示例中,使用以下内容进行测试时:

var myClass = new MyClass();
myClass.DoSomething(new List<string>());

调用扩展类中的第一个方法。 简而言之,这意味着编译器确定更接近传递的参数的签名,并使用它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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