web-dev-qa-db-fra.com

Existe-t-il un moyen en C # de remplacer une méthode de classe par une méthode d'extension?

Il y a eu des occasions où je voudrais remplacer une méthode dans une classe avec une méthode d'extension. Existe-t-il un moyen de le faire en C #?

Par exemple:

public static class StringExtension
{
    public static int GetHashCode(this string inStr)
    {
        return MyHash(inStr);
    }
}

Un cas où j'ai voulu le faire est de pouvoir stocker un hachage d'une chaîne dans une base de données et que cette même valeur soit utilisée par toutes les classes qui utilisent le hachage de la classe de chaîne (c'est-à-dire Dictionnaire, etc.). l'algorithme de hachage .Net intégré n'est pas garanti d'être compatible d'une version du Framework à l'autre, je veux le remplacer par la mienne.

Il y a d'autres cas que j'ai rencontrés où je voudrais remplacer une méthode de classe par une méthode d'extension, donc ce n'est pas seulement spécifique à la classe de chaîne ou à la méthode GetHashCode.

Je sais que je pourrais le faire en sous-classant une classe existante, mais ce serait pratique de pouvoir le faire avec une extension dans de nombreux cas.

95
Phred Menyhert

Non; une méthode d'extension n'a jamais la priorité sur une méthode d'instance avec une signature appropriée et ne participe jamais au polymorphisme (GetHashCode est une méthode virtual).

89
Marc Gravell

Si la méthode a une signature différente, alors cela peut être fait - donc dans votre cas: non.

Mais sinon, vous devez utiliser l'héritage pour faire ce que vous cherchez.

7
Chris Brandsma

Pour autant que je sache, la réponse est non, car une méthode d'extension n'est pas une instance. Cela ressemble plus à une fonctionnalité d'intellisense qui vous permet d'appeler une méthode statique à l'aide d'une instance d'une classe. Je pense qu'une solution à votre problème peut être un intercepteur qui intercepte l'exécution d'une méthode spécifique (par exemple GetHashCode ()) et faire autre chose. Pour utiliser un tel intercepteur (comme celui fourni par Castle Project), tous les objets doivent être instansiés à fabrique d'objets (ou un conteneur IoC dans Castle) afin que leurs interfaces puissent être interceptées via un proxy dynamique généré lors de l'exécution (Caslte vous permet également d'intercepter des membres virtuels de classes)

2
Beatles1692

J'ai trouvé un moyen d'invoquer une méthode d'extension avec la même signature qu'une méthode de classe, mais elle ne semble pas très élégante. En jouant avec les méthodes d'extension, j'ai remarqué un comportement non documenté. Exemple de code:

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

Ce que j'ai remarqué, c'est que si j'appelais une deuxième méthode à l'intérieur d'une méthode d'extension, elle appellerait la méthode d'extension qui correspondait à la signature même s'il y avait une méthode de classe qui correspondait également à la signature. Par exemple, dans le code ci-dessus, lorsque j'appelle ExtFunc (), qui à son tour appelle ele.GetDesc (), j'obtiens la chaîne de retour "Extension GetDesc" au lieu de la chaîne "Base GetDesc" que nous attendons.

Test du code:

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

Cela vous permet de rebondir entre les méthodes de classe et les méthodes d'extension à volonté, mais nécessite l'ajout de méthodes "pass-through" en double pour vous mettre dans la "portée" que vous désirez. Je l'appelle ici portée par manque d'une meilleure Parole. J'espère que quelqu'un pourra me dire comment cela s'appelle réellement.

Vous avez peut-être deviné par mes noms de méthode "pass-through" que j'ai également joué avec l'idée de leur passer des délégués dans l'espoir qu'une ou deux méthodes uniques pourraient servir de pass-through pour plusieurs méthodes avec la même signature. Malheureusement, cela ne devait pas être comme une fois que le délégué était déballé, il choisissait toujours la méthode de classe plutôt que la méthode d'extension, même à l'intérieur d'une autre méthode d'extension. "Portée" n'avait plus d'importance. Je n'ai pas beaucoup utilisé les délégués Action et Func, alors peut-être que quelqu'un de plus expérimenté pourrait comprendre cette partie.

0
DanK