Есть ли способ в C # переопределить метод класса с помощью метода расширения? - PullRequest
94 голосов
/ 22 мая 2009

Были случаи, когда я хотел бы переопределить метод в классе с помощью метода расширения. Есть ли способ сделать это в C #?

Например:

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

Случай, когда я хотел это сделать, - это иметь возможность сохранять хэш строки в базе данных и иметь это же значение для всех классов, которые используют хэш класса строки (т. Е. Словарь и т. Д.). ) Поскольку встроенный алгоритм хеширования .Net не гарантированно совместим от одной версии Framework к другой, я хочу заменить его своей собственной.

В другом случае я столкнулся с тем, что я бы хотел переопределить метод класса с помощью метода расширения, так что это не только специфично для строкового класса или метода GetHashCode.

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

Ответы [ 4 ]

89 голосов
/ 22 мая 2009

Нет; метод расширения никогда не имеет приоритета над методом экземпляра с подходящей сигнатурой и никогда не участвует в полиморфизме (GetHashCode - это метод virtual).

7 голосов
/ 22 мая 2009

Если метод имеет другую сигнатуру, то это можно сделать - так в вашем случае: нет.

Но в противном случае вам нужно использовать наследование, чтобы делать то, что вы ищете.

2 голосов
/ 23 мая 2009

Насколько я знаю, ответ - нет, потому что метод расширения не является экземпляром. Для меня это больше похоже на средство intellisense, которое позволяет вам вызывать статический метод с использованием экземпляра класса. Я думаю, что решением вашей проблемы может быть перехватчик, который перехватывает выполнение определенного метода (например, GetHashCode ()) и делает что-то еще. Чтобы использовать такой перехватчик (как обеспечивает один проект Castle), все объекты должны быть созданы с использованием фабрика объектов (или контейнер IoC в Castle), чтобы их интерфейсы могли быть перехвачены через динамический прокси-сервер, сгенерированный во время выполнения (Caslte также позволяет перехватывать виртуальные члены классов)

0 голосов
/ 23 марта 2019

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

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

Что я заметил, так это то, что если бы я вызывал второй метод изнутри метода расширения, он вызывал бы метод расширения, который соответствовал сигнатуре, даже если был метод класса, который также соответствовал сигнатуре. Например, в приведенном выше коде, когда я вызываю ExtFunc (), которая в свою очередь вызывает ele.GetDesc (), я получаю возвращаемую строку «Extension GetDesc» вместо строки «Base GetDesc», которую мы ожидаем.

Тестирование кода:

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

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

Возможно, вы уже догадались по именам моих "сквозных" методов, которые я также поиграл с идеей передачи им делегатов в надежде, что один или два метода могут действовать как сквозные для нескольких методов с одинаковыми подпись. К сожалению, этого не произошло, поскольку после распаковки делегата он всегда выбирал метод класса, а не метод расширения, даже внутри другого метода расширения. «Сфера» больше не имеет значения. Я не очень часто использовал делегатов Action и Func, так что, возможно, кто-то более опытный мог бы понять эту часть.

...