C# 解释中的部分方法

Partial Methods in C# Explanation

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

您能否提供一个与 LINQ 或类似数据库无关的示例?

部分方法是否与我们在 WinForms 中并在其背后编码时一样,如果我们使用一个方法,它会被编译,但如果我们不使用,它就会被编译器删除?对吗?

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

所以在一个文件中你有:

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 未在某处实现,则删除对它的调用(来自 here):

Partial methods enable the implementer of one part of a class to define a method, similar to an event. The implementer of the other part of the class can decide whether to implement the method or not. If the method is not implemented, then the compiler removes the method signature and all calls to the method. The calls to the method, including any results that would occur from evaluation of arguments in the calls, have no effect at run time. Therefore, any code in the partial class can freely use a partial method, even if the implementation is not supplied. No compile-time or run-time errors will result if the method is called but not implemented.

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

Partial method declarations must begin with the contextual keyword partial and the method must return void.

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

Partial methods are especially useful as a way to customize generated code. They allow for a method name and signature to be reserved, so that generated code can call the method but the developer can decide whether to implement the method. Much like partial classes, partial methods enable code created by a code generator and code created by a human developer to work together without run-time costs.

所以您可能已经生成了调用分部方法的代码(在生成的代码中没有实现定义)并且您可以自由扩展该分部 class 并在需要时实现该分部方法/需要。

作为对该答案评论中 Matt Burlands answer Johnsons 后续问题的补充:

我自己从未创建过它们,但我想到了一些小用途:

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

我还要指出一些差异,这些差异可能是您尝试使用部分方法实现的目标的替代方法。

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

另一个用途可能是为某人提供基本功能。你可以做我称之为 "bottom up" 的事情,这意味着你创造了一些东西,人们将它作为基础,在它之上构建,像工具一样使用它。或者您可以提供它 "top down"。意思是,您已经实现了组件协同工作的方式,但其他人可能会定义这些组件在内部究竟做了什么,例如我想构建一个 table 并且我已经实现了它是一个有四个腿的板,但我将它留给用户来选择腿的形式。这通常与生成的代码一起使用,将生成的代码和 semf 生成的代码写在一个 class 但在不同的 .cs 文件中。

注意

综上所述,这些可能不是实现这些目标的最佳方法。有多种方法可以做那些不需要部分方法的事情,并且可能更适合你想做的事情。但也许这些可以让您了解如何使用分部方法。 这实际上取决于您需要做什么,但了解硬件部分 classes 的工作在将来可能会有用。

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

// --- 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
    }
}

等等