扩展分部方法Extending Partial Methods
总结Summary
此建议旨在消除对 c # 中的方法的签名的所有限制 partial
。This proposal aims to remove all restrictions around the signatures of partial
methods in C#. 目标是扩展一组方案,在这些方案中,这些方法可与源生成器一起使用,并且是更通用的 c # 方法声明形式。The goal being to expand the set of scenarios in which these methods can work with source generators as well as being a more general declaration form for C# methods.
另请参阅 原始分部方法规范。See also the original partial methods specification.
动机Motivation
对于将方法拆分为声明和定义/实现的开发人员,c # 提供了有限的支持。C# has limited support for developers splitting methods into declarations and definitions / implementations.
partial class C
{
// The declaration of C.M
partial void M(string message);
}
partial class C
{
// The definition of C.M
partial void M(string message) => Console.WriteLine(message);
}
方法的一个行为 partial
是:如果定义不存在,则该语言只会擦除对方法的任何调用 partial
。One behavior of partial
methods is that when the definition is absent then the language will simply erase any calls to the partial
method. 实质上,它的行为类似于调用方法,在 [Conditional]
此方法中,条件的计算结果为 false。Essentially it behaves like a call to a [Conditional]
method where the condition was evaluated to false.
partial class D
{
partial void M(string message);
void Example()
{
M(GetIt()); // Call to M and GetIt erased at compile time
}
string GetIt() => "Hello World";
}
此功能的原始动机是以设计器生成的代码的形式生成的源。The original motivation for this feature was source generation in the form of designer generated code. 用户经常编辑生成的代码,因为他们希望挂钩生成的代码的某个方面。Users were constantly editing the generated code because they wanted to hook some aspect of the generated code. 初始化组件后,最值得注意的是 Windows 窗体启动过程的一部分。Most notably parts of the Windows Forms startup process, after components were initialized.
编辑生成的代码很容易出错,因为任何导致设计器重新生成代码的操作都会导致清除用户编辑。Editing the generated code was error prone because any action which caused the designer to regenerate the code would cause the user edit to be erased. partial
方法功能减轻此张力,因为它允许设计器以方法的形式发出挂钩 partial
。The partial
method feature eased this tension because it allowed designers to emit hooks in the form of partial
methods.
设计器可以发出挂钩 partial void OnComponentInit()
,如,开发人员可以为其定义声明或不定义它们。Designers could emit hooks like partial void OnComponentInit()
and developers could define declarations for them or not define them. 在这两种情况下,尽管生成的代码会进行编译,并且对进程感兴趣的开发人员可以根据需要挂钩。In either case though the generated code would compile and developers who were interested in the process could hook in as needed.
这意味着分部方法具有几个限制:This does mean that partial methods have several restrictions:
- 必须具有
void
返回类型。Must have avoid
return type. - 不能具有
out
参数。Cannot haveout
parameters. - 不能 (隐式) 的可访问性
private
。Cannot have any accessibility (implicitlyprivate
).
存在这些限制的原因是,在清除调用站点时,语言必须能够发出代码。These restrictions exist because the language must be able to emit code when the call site is erased. private
由于无法在程序集元数据中公开该成员,因此可将其删除。Given they can be erased private
is the only possible accessibility because the member can't be exposed in assembly metadata. 这些限制还用于限制 partial
可应用方法的方案集。These restrictions also serve to limit the set of scenarios in which partial
methods can be applied.
此处的建议是删除有关方法的所有现有限制 partial
。The proposal here is to remove all of the existing restrictions around partial
methods. 实质上,使它们具有 out
非 void 返回类型或任何类型的可访问性。Essentially let them have out
, non-void return types or any type of accessibility. 这样 partial
的声明将具有定义必须存在的附加要求。Such partial
declarations would then have the added requirement that a definition must exist. 这意味着语言不必考虑擦除调用站点的影响。That means the language does not have to consider the impact of erasing the call sites.
这会扩展方法可参与的一组生成器方案, partial
因此与源生成器功能非常类似。This would expand the set of generator scenarios that partial
methods could participate in and hence link in nicely with our source generators feature. 例如,可使用以下模式定义 regex:For example a regex could be defined using the following pattern:
[RegexGenerated("(dog|cat|fish)")]
partial bool IsPetMatch(string input);
这为开发人员提供了一种简单的声明性方法来选择生成器,并为生成器提供一组非常简单的声明,用于在源代码中查找其生成的输出。This gives both the developer a simple declarative way of opting into generators as well as giving generators a very easy set of declarations to look through in the source code to drive their generated output.
与生成器与以下代码片段挂钩的难度进行比较。Compare that with the difficulty that a generator would have hooking up the following snippet of code.
var regex = new RegularExpression("(dog|cat|fish)");
if (regex.IsMatch(someInput))
{
}
假设编译器不允许生成器修改代码挂钩,这种模式对于生成器而言可能会很大。Given that the compiler doesn't allow generators to modify code hooking up this pattern would be pretty much impossible for generators. 它们需要使用实现中的反射 IsMatch
,或要求用户将其调用站点更改为新方法,并重构 regex 以将字符串文本作为参数传递。They would need to resort to reflection in the IsMatch
implementation, or asking users to change their call sites to a new method + refactor the regex to pass the string literal as an argument. 这种方法非常杂乱。It's pretty messy.
详细设计Detailed Design
语言将更改为允许 partial
使用显式可访问性修饰符对方法进行批注。The language will change to allow partial
methods to be annotated with an explicit accessibility modifier. 这意味着可以将它们标记为 private
、 public
等。This means they can be labeled as private
, public
, etc ...
如果 partial
方法具有显式可访问性修饰符,则该语言将要求声明具有匹配的定义,即使可访问性是 private
:When a partial
method has an explicit accessibility modifier though the language will require that the declaration has a matching definition even when the accessibility is private
:
partial class C
{
// Okay because no definition is required here
partial void M1();
// Okay because M2 has a definition
private partial void M2();
// Error: partial method M3 must have a definition
private partial void M3();
}
partial class C
{
private partial void M2() { }
}
此外,此语言将删除对 partial
具有显式可访问性的方法所显示的所有限制。Further the language will remove all restrictions on what can appear on a partial
method which has an explicit accessibility. 此类声明可以包含非 void 返回类型、 out
参数、 extern
修饰符等 .。。这些签名将具有 c # 语言的完整表现力。Such declarations can contain non-void return types, out
parameters, extern
modifier, etc ... These signatures will have the full expressivity of the C# language.
partial class D
{
// Okay
internal partial bool TryParse(string s, out int i);
}
partial class D
{
internal partial bool TryParse(string s, out int i) { }
}
这显式允许 partial
方法参与 overrides
和 interface
实现:This explicitly allows for partial
methods to participate in overrides
and interface
implementations:
interface IStudent
{
string GetName();
}
partial class C : IStudent
{
public virtual partial string GetName();
}
partial class C
{
public virtual partial string GetName() => "Jarde";
}
编译器将更改它在 partial
方法包含非法元素时发出的错误,而本质上说:The compiler will change the error it emits when a partial
method contains an illegal element to essentially say:
不能
ref
对partial
缺少显式辅助功能的方法使用Cannot useref
on apartial
method that lacks explicit accessibility
当使用此功能时,这将有助于指导开发人员正确的方向。This will help point developers in the right direction when using this feature.
限制:Restrictions:
partial
具有显式辅助功能的声明必须具有定义partial
declarations with explicit accessibility must have a definitionpartial
声明和定义签名必须在所有方法和参数修饰符上都匹配。partial
declarations and definition signatures must match on all method and parameter modifiers. 唯一可能不同的方面是参数名称和属性列表 (这不是新的,而是) 的方法的现有要求partial
。The only aspects which can differ are parameter names and attribute lists (this is not new but rather an existing requirement ofpartial
methods).
问题Questions
部分在所有成员上partial on all members
假设我们要扩展 partial
更友好的源生成器,还应将其扩展为适用于所有类成员吗?Given that we're expanding partial
to be more friendly to source generators should we also expand it to work on all class members? 例如,我们应该能够声明 partial
构造函数、运算符等 .。。For example should we be able to declare partial
constructors, operators, etc ...
解决方法 思路非常不错,但在 c # 9 计划中,我们正努力避免不必要的功能蔓延。Resolution The idea is sound but at this point in the C# 9 schedule we're trying to avoid unnecessary feature creep. 想要解决将此功能扩展到使用现代源生成器的即时问题。Want to solve the immediate problem of expanding the feature to work with modern source generators.
partial
C # 10 版本将对扩展以支持其他成员。Extending partial
to support other members will be considered for the C# 10 release. 可能会考虑此扩展。Seems likely that we will consider this extension.
使用抽象而不是部分Use abstract instead of partial
此建议的关键实质上是确保声明具有相应的定义/实现。The crux of this proposal is essentially ensuring that a declaration has a corresponding definition / implementation. 假设我们要使用, abstract
因为它已是一个可强制开发人员考虑实现的语言关键字?Given that should we use abstract
since it's already a language keyword that forces the developer to think about having an implementation?
解决方法 这是一个关于此操作的正常讨论,但最终决定了它。Resolution There was a healthy discussion about this but eventually it was decided against. 是的,需要熟悉这些要求,但概念却大相径庭。Yes the requirements are familiar but the concepts are significantly different. 如果不这样做,开发人员可以轻松地让开发人员相信它们正在创建虚拟槽。Could easily lead the developer to believe they were creating virtual slots when they were not doing so.