17

根据部分类的MSDN 文档:

部分方法是隐式私有的

所以你可以拥有这个

// Definition in file1.cs
partial void Method1();

// Implementation in file2.cs
partial void Method1()
{
  // method body
}

但你不能拥有这个

// Definition in file1.cs
public partial void Method1();

// Implementation in file2.cs
public partial void Method1()
{
  // method body
}

但这是为什么呢?编译器是否有某些原因无法处理公共部分方法?

4

9 回答 9

27

部分方法必须在编译时完全可解析。如果它们在编译时不存在,则它们在输出中完全丢失。部分方法起作用的全部原因是删除它们对单行调用站点之外的 API 或程序流没有影响(这也是它们必须返回 void 的原因)。

当您向公共 API 添加方法时 - 您正在为其他对象定义合同。由于部分方法的全部意义在于使其成为可选方法,因此您基本上会说:“我有一个可以依赖的合同。哦等等,不过,您不能依赖这种方法。”

为了使公共 API 合理,partial 方法必须要么总是存在,要么总是消失——在这种情况下,它不应该是局部的。

理论上,语言设计者本可以改变部分方法的工作方式,以实现这一点。他们可以使用存根(即:一种什么都不做的方法)来实现它们,而不是从它们被调用的任何地方删除它们。但是,这不会那么有效,并且对于部分方法设想的用例来说是不必要的。

于 2010-01-08T01:47:47.687 回答
8

与其问他们为什么是 private,不如将问题改写为:

  • 如果部分方法不是私有的会发生什么?

考虑这个简单的例子:

public partial class Calculator
{
    public int Divide(int dividend, int divisor)
    {
        try
        {
            return dividend / divisor;
        }
        catch (DivideByZeroException ex)
        {
            HandleException(ex);
            return 0;
        }
    }

    partial void HandleException(ArithmeticException ex);
}

让我们暂时忽略为什么我们将其设为部分方法而不是抽象方法的问题(我会回到那个问题)。重要的是,无论该HandleException方法是否实现,它都能编译并正常工作。如果没有人实现它,这只会吃掉异常并返回 0。

现在让我们更改规则,假设部分方法可以受到保护:

public partial class Calculator
{
    // Snip other methods

    // Invalid code
    partial protected virtual void HandleException(ArithmeticException ex);
}

public class LoggingCalculator : Calculator
{
    protected override virtual void HandleException(ArithmeticException ex)
    {
        LogException(ex);
        base.HandleException(ex);
    }

    private void LogException(ArithmeticException ex) { ... }
}

我们这里有点问题。我们已经“覆盖”了该HandleException方法,只是还没有可以覆盖的方法。我的意思是该方法实际上不存在,它根本没有被编译。

Calculator我们的基础调用是什么意思HandleException?它应该调用派生(覆盖)方法吗?HandleException如果是这样,编译器会为基本方法发出什么代码?它应该变成抽象方法吗?一个空的方法?当派生方法调用时会发生什么base.HandleException?这应该什么都不做吗?提高一个MethodNotFoundException?这里真的很难遵循最小惊喜的原则;几乎你所做的任何事情都会令人惊讶。

或者,HandleException调用时可能什么都不会发生,因为未实现基本方法。不过,这似乎不是很直观。我们的派生类已经实现了这个方法,而基类已经在我们不知道的情况下从它下面拉开了地毯。我可以很容易地想象一些可怜的开发人员把他的头发拉出来,无法弄清楚为什么他的覆盖方法永远不会被执行。

或者也许这段代码根本不应该编译或者应该产生一个警告。但这本身就有很多问题。最重要的是,它违反了部分方法提供的契约,即忽略实现一个方法永远不会导致编译器错误。你有一个基类,它运行得很好,然后,由于有人在应用程序的某个完全不同的部分实现了一个完全有效的派生类,突然你的应用程序就坏了。

而且我什至还没有开始谈论基类和派生类在不同程序集中的可能性。如果您使用包含“公共”部分方法的基类引用程序集,并尝试在另一个程序集的派生类中覆盖它,会发生什么情况?基础方法是否存在?如果最初实现了该方法,并且我们针对它编写了一堆代码,但有人决定删除该实现怎么办?编译器无法从引用类中剔除部分方法调用,因为就编译器而言,该方法从一开始就不存在. 它不存在,它不再存在于已编译程序集的 IL 中。所以现在,简单地通过删除部分方法的实现,它应该没有不良影响,我们已经破坏了一大堆依赖代码。

现在有些人可能会说,“那又怎样,我知道我不会尝试用部分方法来做这些非法的事情。” 您必须了解的是,分部方法(很像分部类)主要用于帮助简化代码生成任务。您不太可能想自己编写部分方法period。另一方面,对于机器生成的代码,代码的使用者实际上很可能希望在不同的位置“注入”代码,而部分方法提供了一种干净的方式来执行此操作。

这就是问题所在。如果由于部分方法引入了编译时错误的可能性,那么您已经创建了代码生成器生成的代码无法编译的情况。这是一个非常非常糟糕的情况。想想如果你最喜欢的设计器工具——比如 Linq to SQL,或者 Winforms 或 ASP.NET 设计器,突然开始生成有时无法编译的代码,你会怎么做因为其他一些程序员创建了一些你以前从未见过的其他类,恰好与部分方法变得有点过于亲密?

最后,它确实归结为一个更简单的问题:公共/受保护的部分方法会添加哪些您已经无法使用抽象方法完成的功能?partials 背后的想法是你可以把它们放在具体的类上,它们仍然可以编译。或者更确切地说,它们不会编译,但它们也不会产生错误,它们将被完全忽略。但是,如果您希望它们被公开调用,那么它们就不再是真正的“可选”了,并且如果您希望它们在派生类中被覆盖,那么您不妨将其设为抽象或虚拟和空。除了混淆编译器和试图理解这一切的可怜的混蛋之外,公共或受保护的部分方法实际上没有任何用处。

因此,团队并没有打开那个潘多拉魔盒,而是说算了吧——公共/受保护的部分方法无论如何都没有多大用处,所以只需将它们设为私有即可。这样我们就可以保持一切安全和理智。它是。只要部分方法保持私有,它们就易于理解且无忧。让我们保持这种状态!

于 2010-03-19T03:05:16.927 回答
6

因为 MS 编译器团队没有实现此功能的要求。

这是 MS 内部发生的一个可能场景,因为 VS 使用代码生成来实现它的许多功能,有一天一位 MS 代码生成开发人员决定他/她需要有部分方法,以便代码生成的 api 可以扩展局外人,这一要求导致编译器团队采取行动并交付。

于 2010-03-24T03:24:09.277 回答
3

如果不定义方法,编译器应该怎么做?

如果您不定义它们,则根本不会编译部分方法。
这只是可能的,因为编译器知道对方法的所有调用在哪里。如果未定义该方法,它将完全删除调用该方法的代码行。(这也是他们只能返回的原因void

如果该方法是公共的,则无法正常工作,因为该方法可能由编译器无法控制的不同程序集调用。

于 2010-01-08T01:02:00.493 回答
2

Reed's and Slak's answers are entirely correct but you seem unwilling to accept their answers.

I will thus try to explain why partial methods are implemented with these restrictions.

Partial methods are for implementing certain sorts of code gen scenarios with maximal efficiency, both in execution time and in meta data overhead. The last part is the real reason for them since they are attempting to make them (in Erics words) "Pay for play".

When partial methods were added the JIT was entirely capable of inlining an empty method, and thus it having zero runtime effort at the call sites. The problem is that even then there is a cost involved which is that the meta data for the class will have these empty methods increasing their size (needlessly) as well as forcing some more effort during the JITing process to deal with optimizing them away.

Whilst you may not worry too much this cost (and indeed many people won't notice it at all) it does make a big difference to code where startup cost matters, or where disk/memory is constrained. You may have noticed the now mandatory use of .Net on windows mobile 7 and the Zune, in these areas bloat on type metadata is a considerable cost.

Thus partial methods are designed such that, if they are never used they have absolutely zero cost, they cease to exist in the output in any way. This comes with some significant constraints to ensure this does not result in bugs.

Taken from the msdn page with my notes.

  • ...the method must return void.
  • Partial methods can have ref but not out parameters.

Otherwise removing the call to them may leave you with an undefined problem of what to replace the assignment with.

  • Partial methods cannot be extern, because the presence of the body determines whether they are defining or implementing.
  • You can make a delegate to a partial method that has been defined and implemented, but not to a partial method that has only been defined.

These follow from the fact the compiler needs to know if it's defined or not, and thus is safe for removal. This leads us to the one you dislike.

  • Partial methods are implicitly private, and therefore they cannot be virtual.

Your metal model of this feature is that, if the compiler knows that a partial method is implemented then it should simply allow the partial method to be public (and virtual for that matter) since it can check that you implemented the method.

Were you to change the feature to do this you have two options:

  1. Force all non private partial methods to require implementation.

    • simple and not likely to involve much effort but then any such methods are no longer partial in the meaningful sense of the original plan.
  2. Any method declared public is simply deleted if it was not implemented in the assembly.

    • This allows the partial removal to be applied
    • It requires quite a lot more effort by the compiler (to detect all references to the method rather than simply needing to look in the composed class itself)
    • The IntelliSense implementation is a bit confused, should the method be shown? sown only when it's been given a definition?
    • Overload resolution becomes much more complex since you need to decide whether a call to such a method with no definition is either a) a compile time failure or b) results in the selection of the next best option if available.
    • Side effects within expressions at the call sites are already complex in the private only case. This is mitigated somewhat by the assumption that partial implementations already exhibit a high degree of coupling. This change would increase the potential for coupling.
    • This has rather complex failure modes in that public methods could be silently deleted by some innocuous change in the original assembly. Other assemblies depending on this one would fail (at compile time) but with a very confusing error (without considerable effort this would apply even to projects in the same solution).

Solution 1 is simply pointless, it adds effort and is of no benefit to the original goals. Worse it may actually hinder the original goals since someone using partial methods this way may not realise they are gaining no reduction in metadata. Also someone may become confused about the errors that result from failing to supply the definition.

That leaves us with solution 2. This solution involves effort (and every feature starts with -100) so it must add some compelling benefit to get it over the -100 (and the additional negatives added for the confusion caused by not the additional edge cases). What scenarios can you come up with to get things positive?

Your motivating example in the comments above was to "have comments/and or attributes in a different file"

The motivation for XML comments was entirely to increase the locality of documentation and code. If their verbosity is high then outlining exists to mitigate this. My opinion is that this is not sufficient to merit the feature.

The ability to push attributes to a separate location is not in my view that useful, in fact I think having to look in two locations is more confusing. The current private only implementation has this problem too, but it is inevitable and again is mitigated somewhat by the assumption that high coupling that is not visible outside of the class is not as bad as high coupling external to the class.

If you can demonstrate some other compelling reason I'm sure it would be interesting, but the negatives to overcome are considerable.

于 2010-03-20T19:53:19.247 回答
2

我已经仔细阅读了您的所有评论,并且我同意大多数结论,但是我有一种情况,我认为在不失初衷的情况下,我认为可以从公共/受保护的部分方法中受益:

我有一个代码生成器,可以生成序列化代码和其他样板代码。特别是,它为每个属性生成一个“重置”方法,以便像 VS 这样的设计者可以将其值恢复为原始值。在此方法的代码中,我生成了对部分方法 Repaint() 的调用。

这个想法是,如果对象想要,它可以为它编写代码然后做一些事情,否则什么都不做,性能是最佳的。

问题是,有时,对象中存在 Repaint 方法的目的不是从生成的代码中调用,此时,当我声明方法主体时,我应该能够将其设为内部、受保护或公共。此时我正在定义方法,是的,我将在此处记录等,而不是在我生成的声明中,而是在我手工制作的声明中。它也在生成的代码中定义的事实不应该影响这一点。

我同意当您声明没有主体的方法时,在部分方法中定义访问修饰符是没有意义的。当您使用主体声明方法时,您应该可以自由地以任何您想要的方式声明它。我认为编译器接受这种情况在复杂性方面没有任何区别。

请注意,在部分类中,这非常好,我可以将其中一个部分声明“裸露”而无需任何访问修饰符,但我可以在同一类的另一个部分声明中指定它们。只要没有矛盾,一切都好。

于 2011-03-30T19:10:08.523 回答
1

partial如果方法没有实现,编译器将删除它。partial根据我的理解,如果方法可以访问,编译器将无法删除这些方法public

//partial class with public modifier
public partial class Sample 
{
    partial void Display();
}

public partial class Sample
{
    partial void Display() { }

}

我们将能够访问public如下方法,这将限制编译器删除partial未实现的方法。

// referred partial method which doesn't have implementation 
var sample = new Sample();
sample.Display();
于 2017-05-11T22:27:19.953 回答
0

原因很简单,部分方法不是公开的,因为它们是实现细节。它们主要是为了支持与设计器相关的场景,而不是作为受支持的公共 API 的一部分。非公共方法在这里工作得很好。

允许部分方法公开是一项功能。功能具有内在成本,包括设计、测试、开发等……部分方法只是在非常紧凑的 Visual Studio 2008 中添加的众多功能之一。它们的范围尽可能小以适应场景以便离开为更紧迫的功能提供空间,例如 LINQ。

于 2010-03-23T22:24:51.033 回答
0

不确定这是否会回答你的问题,它确实回答了我的问题。 部分方法

摘录:

如果您被允许返回某些东西,并且反过来将其用作 CallMyMethods 函数的返回值,那么当未实现部分方法时您会遇到麻烦。

于 2010-01-08T01:00:36.050 回答