Подтвердить что ты не робот

Как методы расширения работают под капотом?

Подрядчик, в котором я работаю, использует extension methods для реализации CRUD в известных внутренних классах, которыми мы владеем. Я говорю, что лучше использовать обычный inheritance по extension methods по следующим причинам.

  • Использование методов расширения запутывает, скрывает и путает источник методов CRUD.
  • Я предполагаю, что extension methods сильно использует reflection (что медленнее).

Его логика: "Он скомпилирован, так быстро". Возможно, я ошибаюсь... но только потому, что он скомпилирован, не означает, что он не использует отражение, и не означает, что он быстрее обычного наследования.

Итак, мои вопросы:

  • Как extension methods работать под капотом?
  • Лучше ли использовать inheritance или extension methods в классах WELL-KNOWN, которые вы СОБСТВЕННЫ?
4b9b3361

Ответ 1

Как методы расширения работают под капотом?

Это просто статические методы; компилятор перезаписывает вызовы типа myObject.MyExtensionMethod() - MyExtensionClass.MyExtensionMethod(myObject).

Лучше ли использовать методы inheretance или extension в классах WELL-KNOWN, которые вы СОБСТВЕННЫ?

Там нет единого ответа на этот вопрос, все зависит от контекста. Но обычно методы расширения наиболее полезны в этих случаях:

  • у вас нет собственного кода для расширенного типа
  • метод нацелен на интерфейс и будет одинаковым для всех реализаций этого интерфейса (например, IEnumerable<T> и методы расширения Linq)

Ответ 2

Я предполагаю, что методы расширения сильно используют отражение (которое медленнее).

Нет. Методы расширения разрешаются во время компиляции, не требуется никакого отражения.
Это отрицает ваши проблемы с производительностью.

Лучше ли использовать методы inheretance или расширения?

Я бы тоже сказал. Используйте репозиторий (DAL). Сущность должна быть настойчиво-агностической (так: нет наследования из базы, которая делает CRUD), и не претендует на участие в ней (без расширений).

Вы правы, что "использование методов расширения запутывает и путает источник методов CRUD", но наследование не является решением.

Ответ 4

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

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

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

Ответ 5

В ответ на первый вопрос:

Под капотом расширения действуют как делегаты, так что void MyExtension (этот объект obj) можно переписать как Action MyDelegate. Однако там, где они отличаются друг от друга, он имеет синтаксис при вызове, поскольку действие должно обертывать объект, тогда как расширения можно вызывать, как если бы он был членом самого объекта, хотя под капотом это не так (и у него нет прямой доступ к частным или защищенным членам объекта, если на то пошло).

В ответ на второй вопрос:

Я обычно резервирую расширения для любых классов, в которых я не владею, или интерфейсов.

Например, скажем, у вас есть интерфейс IAnimal

Public Interface IAnimal
{
    void Speak();
    void RunToOwnerWhenCalled();
}

И следующие классы

public class Mammal
{
    public virtual void AnswerWhatAmI()
    {
        Console.WriteLine("I'm a mammal");
    }
}

public class Dog:Mammal, IAnimal
{
    public void Speak()
    {
        Console.WriteLine("arf");
    }

    public void RunToOwnerWhenCalled()
    {
        Console.WriteLine("Comming");
    }
}

public class Cat:Mammal, IAnimal
{
    public void Speak()
    {
        Console.WriteLine("meow");
    }

    public void RunToOwnerWhenCalled()
    {
        Console.WriteLine("I don't know you");
    }
}

Тогда у вас может быть расширение, например

Public static void CallAnimal(this IAnimal animal)
{
    animal.RunToOwnerWhenCalled();
}

И такой метод, как

Public static void Main
{
    Cat cat = new Cat();
    cat.CallAnimal();
}

и результат отобразит ответ "Я не знаю вас" на консоли в консоли.

Рассмотрим еще один класс

Public class Human:Mammal
{
    Public Human():base()
    {
        Console.WriteLine("To be more specific, I am a human.");
    }
}

У этого класса не будет расширения CallAnimal, так как оно не влияет на интерфейс IAnimal, хотя это тип млекопитающих.

Ответ 6

Под капотом метод расширения похож на обычный метод, и объект, который он вызвал, передается в качестве первого параметра (параметр this). Там нет ничего особенного в методе расширения, это просто синтаксическая конфета.

Хорошая практика - избегать методов расширения, когда это возможно. Они делают код менее читаемым и менее объектно-ориентированным.

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