Eu descobri uma maneira de invocar um método de extensão com a mesma assinatura de um método de classe, mas não parece muito elegante. Ao brincar com os métodos de extensão, notei alguns comportamentos não documentados. Código de amostra:
public static class TestableExtensions
{
public static string GetDesc(this ITestable ele)
{
return "Extension GetDesc";
}
public static void ValDesc(this ITestable ele, string choice)
{
if (choice == "ext def")
{
Console.WriteLine($"Base.Ext.Ext.GetDesc: {ele.GetDesc()}");
}
else if (choice == "ext base" && ele is BaseTest b)
{
Console.WriteLine($"Base.Ext.Base.GetDesc: {b.BaseFunc()}");
}
}
public static string ExtFunc(this ITestable ele)
{
return ele.GetDesc();
}
public static void ExtAction(this ITestable ele, string choice)
{
ele.ValDesc(choice);
}
}
public interface ITestable
{
}
public class BaseTest : ITestable
{
public string GetDesc()
{
return "Base GetDesc";
}
public void ValDesc(string choice)
{
if (choice == "")
{
Console.WriteLine($"Base.GetDesc: {GetDesc()}");
}
else if (choice == "ext")
{
Console.WriteLine($"Base.Ext.GetDesc: {this.ExtFunc()}");
}
else
{
this.ExtAction(choice);
}
}
public string BaseFunc()
{
return GetDesc();
}
}
O que percebi foi que, se chamasse um segundo método de dentro de um método de extensão, ele chamaria o método de extensão que correspondia à assinatura, mesmo que houvesse um método de classe que também correspondesse à assinatura. Por exemplo, no código acima, quando chamo ExtFunc (), que por sua vez chama ele.GetDesc (), obtenho a string de retorno "Extension GetDesc" em vez da string "Base GetDesc" que esperaríamos.
Testando o código:
var bt = new BaseTest();
bt.ValDesc("");
//Output is Base.GetDesc: Base GetDesc
bt.ValDesc("ext");
//Output is Base.Ext.GetDesc: Extension GetDesc
bt.ValDesc("ext def");
//Output is Base.Ext.Ext.GetDesc: Extension GetDesc
bt.ValDesc("ext base");
//Output is Base.Ext.Base.GetDesc: Base GetDesc
Isso permite que você alterne entre métodos de classe e métodos de extensão à vontade, mas requer a adição de métodos de "passagem" duplicados para chegar ao "escopo" desejado. Estou chamando de escopo aqui por falta de uma palavra melhor. Espero que alguém possa me informar como realmente se chama.
Você deve ter adivinhado por meus nomes de método de "passagem" que também brinquei com a ideia de passar delegados para eles na esperança de que um único método ou dois pudessem atuar como passagem para vários métodos com a mesma assinatura. Infelizmente, não deveria ser assim, uma vez que o delegado era descompactado, ele sempre escolhia o método de classe em vez do método de extensão, mesmo dentro de outro método de extensão. "Escopo" não importava mais. Eu não usei delegados Action e Func muito embora, então talvez alguém mais experiente possa descobrir essa parte.