Как принудительно использовать метод расширения вместо метода экземпляра с параметрами?
Мне сложно заставить компилятор С# вызвать метод расширения, который я создал, поскольку вместо этого он предпочитает метод экземпляра с аргументом 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.");
}
}
В моей примерной программе:
pubic 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", ...
") будет с благодарностью.
Ответы
Ответ 1
Вы не можете использовать расширения для "захвата" существующих методов класса.
Если вызов работает без расширения, поведение не должно меняться при добавлении расширения. Причина этого заключается в том, что существующий код не должен прерываться, введя расширение позже.
Для этого вам нужно использовать другое имя или использовать типы параметров, отличные от метода класса.
Ответ 2
Вы не можете напрямую. Метод в целевом экземпляре всегда предпочтительнее метода расширения. только способ сделать это (сохраняя имена одинаковыми и т.д.), Использовать подход CExtensions.Trace
(в вопросе).
В некоторых случаях здесь может быть использован некоторый базовый класс C
или интерфейса, который реализуется C
, но , у которого нет a Trace
, и re введите переменную и добавьте перегрузку на CExtensions
, т.е.
IFoo foo = c;
foo.Trace(...);
Ответ 3
Tuple<string, decimal>
не совпадает с KeyValuePair<string, decimal>
. Следовательно, KeyValuePair<string, decimal>
передается в качестве объекта, поэтому используется метод member с params object[] args
.
Фактически KeyValuePair
является структурой.
Ответ 4
Вы можете сделать это с помощью именованных аргументов:
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)
});
Ответ 5
Я думаю, вы могли бы изменить первый тип параметра или имя функции (TraceFormat
?)
Эта версия params выглядит очень жадной, поэтому, если вы сохраните первый аргумент как строку, он всегда будет улавливать все вызовы и игнорировать метод расширения, который я считаю.