Как методы расширения работают под капотом? - PullRequest
13 голосов
/ 10 января 2012

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

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

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

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

  1. Как extension methods работает под капотом?
  2. Лучше ли использовать inheritance или extension methods на ХОРОШИХ ИЗВЕСТНЫХ классах, которые ВЫ СВОИМИ?*

Ответы [ 6 ]

19 голосов
/ 10 января 2012

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

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

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

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

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

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

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

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

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

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

2 голосов
/ 10 января 2012

Описание

Extension Methods - это языковая функция. Из этого компилятор делает обычный IL (он же MSIL или CIL) код. Отражение не требуется.

Дополнительная информация

1 голос
/ 17 января 2013

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

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

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

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

Например, скажем, у вас есть интерфейс 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, даже если это тип Mammal.

1 голос
/ 10 января 2012

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

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

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

0 голосов
/ 10 января 2012

Под капотом метод расширения подобен обычному методу, и объект, к которому он вызывается, передается в качестве первого параметра (параметр this). В методе расширения нет ничего особенного, это просто синтаксис candy .

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

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

...