Как заставить использование метода расширения вместо метода экземпляра с params? - PullRequest
11 голосов
/ 08 ноября 2011

У меня проблемы с тем, чтобы компилятор C # вызывал метод расширения, который я создал, поскольку он предпочитает метод экземпляра с аргументом params.

Например, скажем, у меня есть следующий класс и его метод:

public class C
{
    public void Trace(string format, params object[] args)
    {
         Console.WriteLine("Called instance method.");
    }
}

А и расширение:

public static class CExtensions
{
    public void Trace(this C @this, string category, string message, params Tuple<string, decimal>[] indicators)
    {
        Console.WriteLine("Called extension method.");
    }
}

В моем примере программы:

public void Main()
{
    var c = new C();

    c.Trace("Message");
    c.Trace("Message: {0}", "foo");
    c.Trace("Category", "Message", new KeyValuePair<string, decimal>("key", 123));
}

Все звонки распечатать Called instance method..

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

Из того, что я понял, компилятор предпочтет методы экземпляра перед методами расширения, но является ли это единственным правилом? Это означало бы, что любой класс с методом, который выглядит как Method(string format, params object[] args), не может иметь методы расширения с первым параметром типа string.

Будем весьма благодарны за любые объяснения причин такого поведения или способа его обойти (то есть , а не"просто позвоните CExtensions.Trace(c, "Category", ...").

Ответы [ 5 ]

5 голосов
/ 08 ноября 2011

Вы не можете использовать расширения для "захвата" существующих методов класса.

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

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

4 голосов
/ 08 ноября 2011

Вы не можете напрямую . Метод на целевом экземпляре всегда предпочтительнее метода расширения. только способ сделать это (при сохранении имен и т. Д.) - это использовать подход CExtensions.Trace (в вопросе).

В некоторых случаях уловка заключается в том, чтобы использовать некоторый базовый класс C или интерфейс, который реализует C, но , который не имеет a Trace метода, и повторно введите переменную и добавьте перегрузку на CExtensions, т.е.

IFoo foo = c;
foo.Trace(...);
2 голосов
/ 08 ноября 2011

Вы можете сделать это с именованными аргументами:

c.Trace(
    "Category", 
    "Message", 
    indicators: new Tuple<string, decimal>("key", 123));

, но вы потеряете функциональность params и вам потребуется явно передать массив для аргумента индикаторов, как показано ниже:

c.Trace(
    "Category",
    "Message",
    indicators: new Tuple<string, decimal>[] 
    {
        new Tuple<string, decimal>("key", 123),
        new Tuple<string, decimal>("key2", 123)
    });
2 голосов
/ 08 ноября 2011

Tuple<string, decimal> не совпадает с KeyValuePair<string, decimal>.Следовательно, KeyValuePair<string, decimal> передается как объект, поэтому используется метод члена с params object[] args.

Фактически KeyValuePair является структурой .

0 голосов
/ 08 ноября 2011

Полагаю, вы можете изменить первый тип параметра или имя функции (TraceFormat?)

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

...