Общие методы расширения в C #: что произойдет в этом крайнем случае? - PullRequest
6 голосов
/ 26 июля 2010

В моем недавнем моем вопросе я узнал, что если существует более одного метода расширения с ограничениями, соответствующими данному типу, будет выбран наиболее конкретный. Это заставило меня задуматься - как компилятор определяет, какой из них является «более конкретным»? И каков будет результат?

Допустим, у меня есть следующие классы:

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(), поскольку он "ближе" к переданному типу. Что меня тогда интересовало, так это «что означает ближе в этом контексте? Как будет реагировать компилятор, если ограничения происходят из двух разных цепочек наследования? Почему?»

Ответы [ 4 ]

6 голосов
/ 26 июля 2010

Класс расширений не будет компилироваться, в этом случае - вы не можете перегружать методы, основанные исключительно на общих ограничениях.

Если вы поместите два метода расширения в разные классы, тогда вызов код не будет компилироваться - это будет неоднозначный вызов, поскольку ни один из методов не будет "лучше", чем другой ... в обоих случаях аргумент универсального типа будет выводиться как MyClass, поэтому простобыть двумя преобразованиями из MyClass в MyClass, ни одно из которых не лучше другого.

По сути, это всего лишь частный случай перегрузки, когда вы выяснили, что методы экземпляра не применимы.Недавно я написал статью о перегрузке , которая может оказаться полезной - она ​​не вызывает этот конкретный случай, но указывает на соответствующие биты спецификации, если вы хотите посмотреть подробнее.

1 голос
/ 26 июля 2010

Он вообще не будет компилироваться и выдавать ошибку времени компиляции, говоря, что вызов между этими двумя методами неоднозначен.

Тип "MyExtensions" уже определяет член под названием "DoSomething" с одинаковые типы параметров.

EDIT

Вот почему компилятор выдает такую ​​ошибку. Методы расширения являются просто синтаксическими сахарами, и все, что они делают, это обеспечивают беглость и читабельность любого типа.

Проверьте этот код ..

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

Компилятор заменяет этот код следующим образом.

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

В вашем случае компилятор запутывается, поскольку есть две совпадающие сигнатуры для вызова метода, и он выдает указанную ошибку.

0 голосов
/ 26 июля 2010

Рассмотрим следующий пример:

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>());

Первый метод в классе расширений вызывается. Короче говоря, это означает, что компилятор определяет сигнатуру, которая ближе к переданным аргументам, и использует ее.

0 голосов
/ 26 июля 2010

Общие ограничения не рассматриваются как часть сигнатуры метода.Эти два метода рассматриваются компилятором как методы с одинаковой сигнатурой.Таким образом, вы получите ошибку компиляции о том, что метод 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 */ }
...