为什么不在嵌套类中允许扩展方法定义?

Aar*_*ide 10 c# extension-methods

我会发现在嵌套类中为类编写我的exensions是方便/合理的.主要原因是我可以简单地命名该类,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);
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

而现在我必须创建一个单独的独立课程.

更新/注意:我没想到它是一个内部类会影响扩展方法的可用范围.我只想解决实际编码问题一个单独的具有单独名称的类.

Zai*_*sud 5

这里的关键点是嵌套类可以访问外部类中的私有字段.

以下代码有效:

public class Foo
{
    private bool _field;

    public static class Extensions
    {
        public static bool GetField(Foo foo)
        {
            return foo._field;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这里你明确地传入了一个类的实例,并允许一个静态方法访问一个私有字段...似乎合理:

bool fieldValue = Foo.Extensions.GetField(new Foo());
Run Code Online (Sandbox Code Playgroud)

但是,虽然扩展方法只是静态方法的替代语法,但它们的调用方式与非静态实例方法相同.

现在,如果在嵌套类中允许扩展方法,它们实际上可以访问私有字段,并且它们将更接近实例方法.这可能会导致一些意想不到的后果.

总之,如果允许这样做:

public class Foo
{
    private bool _field;

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

然后你可以编写下面的代码,使扩展方法的行为更像一个实例方法,它应该是:

var foo = new Foo();
var iGotAPrivateField = foo.GetField();
Run Code Online (Sandbox Code Playgroud)

根据评论进行编辑

为什么扩展方法等同于实例方法是个坏主意?

Eric Lippert的话中:

所以,是的,经常听到的批评"扩展方法不是面向对象"是完全正确的,但也相当无关紧要.扩展方法当然不是面向对象的.他们将操纵数据的代码放在远离声明数据的代码的位置,它们不能破坏封装并与它们看起来是方法的对象的私有状态交谈,它们不能很好地继承,等等.它们是一种方便的面向对象服装的程序编程.

  • 您后面的示例有什么问题?如果GetField是在Foo中声明的普通方法,那么肯定可以访问_field。同样,如果要调用`Foo.Extensions.GetField(foo)`,则希望它也能访问`_field`。那么为什么不应该使用扩展方法呢?可以肯定的是,如果我正在设计语言,则嵌套类中的扩展方法仅在紧邻的周围类中可用,但是这种限制将增强扩展方法作为功能的有用性。 (2认同)
  • 类中的代码可以决定类的私有字段中包含的状态如何暴露给外界。如果不希望公开某些状态,请不要包括公开其的静态方法。请注意,如果上述扩展方法使用Bar作为其this参数,则只能访问与Foo相关的变量和类型,而不能访问Bar的变量和类型。此外,我希望允许在嵌套类中声明扩展方法的最大原因是允许将其范围限制为外部类(例如... (2认同)
  • 在我的示例中,在 `IEnumerable` 上使用扩展方法需要有一个 *something 的实例来实现 IEnumerable*,但不一定是一个想要使用扩展方法的类的实例 *。 (2认同)

Nik*_*wal 1

我猜想不允许这个事情背后的想法是因为扩展方法适用于跨命名空间的所有实体。

如果您将创建一个嵌套的扩展方法类,那么它将仅适用于嵌套它的类。在这种情况下,没有必要创建扩展方法。那么任何正常的非扩展方法都可以。