C#解释中的分部方法



我很难理解分部方法的用法。

你能提供一个与LINQ或类似数据库的东西无关的例子吗?

分部方法是否与我们在WinForms中并在其后面进行编码时的情况相同?如果我们使用某个方法,它会被编译,但如果我们不使用,它就会被编译器删除?这是正确的吗?

当您有一个分部类时,您可以在一个文件中定义方法的签名,并在另一个文件中将其实现。这是一种局部方法。

因此,在一个文件中:

partial class Foo
{
    partial void Bar();  // no implementation
    public void DoSomething()
    {
        // do some stuff...
        Bar();    // this will be removed if Bar isn't implemented in another partial class
        // do something else...
    }
}

在另一个例子中,你有

partial class Foo
{
    partial void Bar()
    {
        // do something...
    }
}

这使得第一个文件调用Bar而不必担心是否实现了Bar。如果Bar没有在某个地方实现,那么对它的调用将被删除(从这里):

分部方法使类的一部分的实现者能够定义一个方法,类似于事件。类的其他部分的实现者可以决定是否实现该方法。如果未实现该方法,则编译器将删除该方法签名和对该方法的所有调用。对方法的调用,包括对调用中的参数求值所产生的任何结果,在运行时都没有效果。因此,分部类中的任何代码都可以自由使用分部方法,即使没有提供实现。如果调用但未实现该方法,则不会导致编译时或运行时错误。

分部方法必须返回void,否则如果不实现该方法,则删除所有方法调用是不安全的:

分部方法声明必须以上下文关键字分部开头,并且该方法必须返回void。

与分部类一样,主要用途是处理生成的代码:

分部方法作为自定义生成代码的一种方式尤其有用。它们允许保留方法名称和签名,这样生成的代码可以调用该方法,但开发人员可以决定是否实现该方法。与分部类非常相似,分部方法使代码生成器创建的代码和人类开发人员创建的代码能够在没有运行时成本的情况下协同工作。

因此,您可能已经生成了对分部方法进行调用的代码(在生成的代码中定义为未实现),如果您想要/需要,您可以自由扩展该分部类并实现该分部方法。

以下是我在自己的编程中使用的一个示例。。。作为一名教师,我经常向我的同学们提供代码样本。然而,我希望他们一步一步地实现他们的编码项目,随着时间的推移,使其变得越来越复杂。更具体地说,假设我为他们提供了运行菜单的代码,以测试和驱动他们需要实现的类。在步骤1中,菜单很简单。然后,随着每一个新的步骤,都会添加更多的菜单项来测试越来越多的类功能。因此,最初,我为他们提供了一个单一的文件来制定一个简单的菜单,然后随着他们走向一个完整的解决方案,我向他们提供了更多的文件来驱动和检查他们的新编程。可以这样做:

// --- File MenuStep1.cs ---
partial class Menu
{
    // This array is populated with more and more items at every new steps
    readonly List<MenuItem> MenuItems = new List<MenuItem>();
    public void Show()
    {
        // Code to show menu here
    }
    // Suppose we have a Main here, but that's not necessary
    public static void Main()
    {
        new Menu().Show();   
    }
    // These are hooking methods to add menu items later
    partial void InitStep2();
    partial void InitStep3();
    partial void InitStep4();
    public Menu()
    {
        InitStep1();
        InitStep2();
        InitStep3();
        InitStep4();
    }
    void InitStep1()
    {
        // Code that adds menu items, but only for step 1
    }
}

请注意,由于部分方法InitStep2、3和4尚未定义,因此不会调用它们(甚至不会在中编译它们)。稍后,我为他们提供了自动扩展菜单的文件,如下所示:

// --- File MenuStep2.cs ---
partial class Menu
{
    partial void InitStep2()
    {
        // Code that adds more menu items
    }
}

// --- File MenuStep3.cs ---
partial class Menu
{
    partial void InitStep3()
    {
        // Code that adds more menu items
    }
}

等等。

除了Matt Burlands之外,Johnsons在该答案的评论中回答了后续问题:

我自己从来没有创造过它们,但我很少想到它们的小用途:

  • 使用分部方法来craete类似于抽象类或接口的东西。基本上,通过这种方式拥有"可选方法"是可能的,这样就可以删除方法实现,而不必删除它们各自的调用并最终重新实现
  • 使用分部方法能够在同一基础上构建具有不同实现的多个项目。您可以有一个构建过程,允许您在不同的实现之间进行选择,这些实现反映在不同的分部类中。"基类"将具有方法签名,而不同的"实现类"、"implementtA.cs"one_answers"implementB.cs"将具有不同的实现。通过这种方式,您可以创建例如Develop-Test-and-Release实现或User1-User2和User3实现。很酷的一点是,您不必在运行时指定这些东西。相反,您可以分发已经指定到该程度的"干净"版本

我还想指出一些差异,这些差异可能是您试图用分部方法获得的结果的替代方案。

  • 事件-分部方法可以像事件一样使用。如果没有实现它们,它们将被忽略(它们甚至不在编译结果中)。但事件是有区别的。虽然一个事件可以有多个处理程序,但只能实现一次分部方法
  • 抽象类和接口-分部类/方法可以像抽象类一样使用,而不必实现方法并将其留空。如果你知道这种情况在给定的抽象类中经常发生(首先检查你的体系结构,很明显,它可能有缺陷),你可以使用分部方法

另一种用途可能是向某人提供基本功能。你可以这样做,我称之为"自下而上",意思是你把一些东西砸出来,人们把它作为基础,在它上面建造,像工具一样使用它。或者你可以提供它"自上而下"。这意味着,你已经实现了组件协同工作的方式,但其他人可能会定义这些组件在内部的确切功能。例如,我想建立一个表,我已经实现了它是一个有四条腿的板,但我让用户选择腿的形式。这通常与生成的代码一起使用,以在一个类中但在不同的.cs文件中写入生成的和semf生成的代码

注意

尽管如此,这些可能不是实现这些目标的最佳方式。有多种方法可以做那些不需要分部方法的事情,而且可能更适合你想做的事情。但也许这些方法可以让你了解如何使用分部方法。这实际上取决于您需要做什么,但了解hw分部类的工作方式在未来可能会很有用。

最新更新