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

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

Какова техническая причина запретить что-то вроде:

public class Foo
{
   ObjectSet<Bar> Bars { get; set; }

   public static class Extensions
   {
      public static Bar ByName(this ObjectSet<Bar> bars, string name)
      {
         return bars.FirstOrDefault(c => c.Name == name);
      }
   }
}

В то время как теперь я должен создать отдельный класс свободного стояния.

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

Ответ 1

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

Так работает следующий код:

public class Foo
{
    private bool _field;

    public static class Extensions
    {
        public static bool GetField(Foo foo)
        {
            return foo._field;
        }
    }
}

Здесь вы явно передаете экземпляр класса, и статическому методу разрешен доступ к закрытому полю... кажется разумным:

bool fieldValue = Foo.Extensions.GetField(new Foo());

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

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

В итоге, если это было разрешено:

public class Foo
{
    private bool _field;

    public static class Extensions
    {
        public static bool GetField(*this* Foo foo) // not allowed, compile error.
        {
            return foo._field;
        }
    }
}

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

var foo = new Foo();
var iGotAPrivateField = foo.GetField();

Редактировать в результате комментариев

Почему плохая идея, чтобы методы расширения были эквивалентны методам экземпляра?

В словах Эрика Липперта (выделение мое):

Итак, да, часто звучащая критика о том, что "методы расширения не являются объектно-ориентированными", является полностью правильной, но также довольно неактуальной. Методы расширения, безусловно, не являются объектно-ориентированными. Они помещают код, который манипулирует данными, далеко от кода, который объявляет данные, они не могут нарушать инкапсуляцию и общаться с закрытым состоянием объектов, для которых они кажутся методами, они плохо работают с наследованием и так далее. Это процедурное программирование в удобной объектно-ориентированной одежде.

Ответ 2

Нет технической причины для этого - просто практично. Если у вас есть метод расширения, ограниченный по объему для одного класса, просто определите его как обычный статический метод в своем классе и удалите 'this'. Расширения предназначены для совместного использования в нескольких классах.

Ответ 3

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

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