Ответ 1
Если вы просто нажмете new SomeClass(1, "a")
на ITupleMatchable<int, string>
, он будет работать нормально:
var obj = (ITupleMatchable<int, string>)new SomeClass(1, "a");
obj.Match().With(1, "a");
Помните, что ваша переменная obj
имеет тип времени SomeClass
. Компилятор может "легче" сопоставить фактический класс с третьим методом расширения (который совместим с любым типом), чем он может, если посмотреть на реализацию интерфейса SomeClass
, а затем сопоставить его, например, второй метод расширения.
Но если вы предоставите параметр this
в качестве фактического типа интерфейса, то второй метод расширения лучше подходит, потому что он именно тот тип, который ищет метод, а не более широкий "любой тип". То есть это более конкретная декларация, и поэтому она "лучше".
Обратите внимание, что после обнаружения набора кандидатов методов расширения (с помощью правил, относящихся к пространству имен и т.д.), фактический метод определяется с использованием нормального разрешения перегрузки. То есть что хотя бы один метод в вашем классе MatcherExtensions
является подходящим методом расширения, тогда компилятор переходит с нормальными правилами разрешения перегрузки, чтобы выбрать среди них. Эти правила можно найти в спецификации С# 5.0, раздел 7.5.3
.
Вкратце: прежде чем применять правила разрешения перегрузки (действительно, чтобы определить, какие методы даже подходят), обратите внимание, что компилятор уже определил параметры типового типа. Таким образом, поскольку он оценивает разрешение перегрузки, он смотрит на Match(SomeClass item)
и Match(ITupleMatchable<int, string> item)
. Надеюсь, когда вы это уделите, вы поймете, почему, если переменная имеет тип SomeClass
, компилятор выбирает ваше третье расширение предпочтительно над вторым, а наоборот, если тип ITupleMatchable<int, string>
.