C# 是否存在私有扩展方法?

C# 是否存在私有扩展方法?,c#,extension-methods,encapsulation,C#,Extension Methods,Encapsulation,假设我需要一个简单的私有助手方法,直观地说,在代码中它作为一个扩展方法是有意义的。有没有办法将该助手封装到实际需要使用它的唯一类中 例如,我尝试以下方法: class Program { static void Main(string[] args) { var value = 0; value = value.GetNext(); // Compiler error } static int GetNext(this int i

假设我需要一个简单的私有助手方法,直观地说,在代码中它作为一个扩展方法是有意义的。有没有办法将该助手封装到实际需要使用它的唯一类中

例如,我尝试以下方法:

class Program
{
    static void Main(string[] args)
    {
        var value = 0;
        value = value.GetNext(); // Compiler error
    }

    static int GetNext(this int i)
    {
        return i + 1;
    }
}
编译器没有“看到”扩展方法。错误是:

扩展方法必须在非泛型静态类中定义

很公平,所以我将其封装在自己的类中,但仍然封装在它所属的对象中:

class Program
{
    static void Main(string[] args)
    {
        var value = 0;
        value = value.GetNext(); // Compiler error
    }

    static class Extensions
    {
        static int GetNext(this int i)
        {
            return i + 1;
        }
    }
}
还是没有骰子。现在,错误状态为:

扩展方法必须在顶级静态类中定义;扩展是一个嵌套类


这一要求是否有令人信服的理由?有些情况下,助手方法确实应该被私有封装,如果助手方法是扩展方法,则代码会更干净、更可读/更受支持。对于这两个相交的情况,我们是否都可以满足,或者我们必须选择一个而不是另一个?

此代码编译并运行:

static class Program
{
    static void Main(string[] args)
    {
        var value = 0;
        value = value.GetNext(); // Compiler error
    }

    static int GetNext(this int i)
    {
        return i + 1;
    }
}
注意
静态类程序
行,这是编译器所说的需要的

这一要求是否有令人信服的理由

这个问题问错了。语言设计团队在设计此功能时提出的问题是:

是否有令人信服的理由允许在嵌套静态类型中声明扩展方法

由于扩展方法是为了使LINQ工作而设计的,并且LINQ没有扩展方法对某个类型私有的场景,所以答案是“不,没有这样令人信服的理由”


通过消除在静态嵌套类型中放置扩展方法的能力,无需考虑、争论、设计、指定、实现、测试、记录、交付给客户,或使其与C#未来的每个功能兼容。这大大节约了成本。

我相信这是他们实现扩展方法编译的方式

查看IL,它们似乎为方法添加了一些额外的属性

.method public hidebysig static int32 GetNext(int32 i) cil managed
{
    .custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()
    .maxstack 2
    .locals init (
        [0] int32 num)
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: ldc.i4.1 
    L_0003: add 
    L_0004: dup 
    L_0005: starg.s i
    L_0007: stloc.0 
    L_0008: br.s L_000a
    L_000a: ldloc.0 
    L_000b: ret 
}

我们可能遗漏了一些非常基本的东西,但这些东西无法使它发挥作用,这就是为什么要实施限制。也可能只是他们想要强制编码实践。不幸的是,它不能工作,必须在顶级静态类中。

我相信在一般情况下,最好的方法是使用
内部静态
扩展方法的
内部静态
类。由于它将位于您自己的程序集中,因此您需要阻止使用扩展的唯一人员是程序集的作者-因此一些显式命名的命名空间(如
My.Extensions.ForFoobarOnly
)可能足以提示以避免误用

文章中涉及的最小
内部
限制

该类必须对客户端代码可见。。。方法的可见性至少与包含类的可见性相同


注意:无论如何,我都会公开扩展以简化单元测试,但会放入一些显式命名的名称空间,如
Xxxx.yyy.Internal
,这样程序集的其他用户就不会期望支持/调用这些方法。基本上依赖于编译时强制以外的约定。

这是从microsoft msdn上的一个示例中获取的。Extesnion方法必须在静态类中定义。查看静态类是如何在不同的命名空间中定义并导入的。您可以在这里看到示例


虽然问题得到了正确的回答,但我想添加一个简单的示例。 由于扩展方法(如果它们是私有的)没有多大意义,因为在包含扩展类的外部不可访问,所以您可以将它们放置在自定义命名空间中,并且仅在您自己(或任何其他)的命名空间中使用该命名空间,这使得扩展方法在全局范围内不可访问

namespace YourOwnNameSpace
{
    using YourExtensionNameSpace;

    static class YourClass
    {
        public static void Test()
        {
            Console.WriteLine("Blah".Bracketize());
        }
    }
}

namespace YourOwnNameSpace
{
    namespace YourExtensionNameSpace
    {
        static class YourPrivateExtensions
        {
            public static string Bracketize(this string src)
            {
                return "{[(" + src + ")]}";
            }
        }
    }
}

您必须定义两次名称空间,并将扩展名称空间嵌套在另一个名称空间中,而不是类所在的位置,您必须通过
使用
来使用它。像这样,扩展方法在您不使用它的地方将不可见。

阅读第一条错误消息所说的内容,它希望您的程序类是静态的,您是否尝试将其设置为静态并尝试代码?相关问题:@Bearcat9425,这将*修复“这是一个特定的用例场景,但并不普遍适用。我想这个问题是针对通用性的,而不是针对示例控制台应用程序。我想你能得到的最好结果是内部静态类/内部扩展方法,并将其隐藏在某个模糊的名称空间中…@AlexeiLevenkov:你可能是对的,在大多数情况下,我只是碰巧得到了这个结果。似乎我的大多数“私有”助手都是抽象(依赖注入)程序集,其内部对域的其余部分甚至都不可见,在这种情况下,很容易使扩展成为顶级的。直到最近才出现这种情况。我承认,在我做作的例子中,这种方法很有效。但是使类保持静态并不总是可行的选择。对于域模型来说,这可能会造成很大的问题。这就是为什么它们被称为“扩展方法”,而最佳实践是将它们定义在一个单独的静态类中,放在不同的命名空间下。“最佳实践”是一个相对的术语。封装也是一种最佳实践。在某些情况下,将助手函数(在本例中是扩展方法)放在单独的类(或完全单独的命名空间)中会导致泄漏的抽象,将该助手函数与唯一有帮助的对象的内部紧密耦合。我不知道您的域内部,很可能您是对的。然而,如果它们只是一些辅助函数,那么它们可能不应该是扩展方法
namespace YourOwnNameSpace
{
    using YourExtensionNameSpace;

    static class YourClass
    {
        public static void Test()
        {
            Console.WriteLine("Blah".Bracketize());
        }
    }
}

namespace YourOwnNameSpace
{
    namespace YourExtensionNameSpace
    {
        static class YourPrivateExtensions
        {
            public static string Bracketize(this string src)
            {
                return "{[(" + src + ")]}";
            }
        }
    }
}