diff --git a/docfx.json b/docfx.json index 4515a0c8ddaa6..1cfb7a4c9d176 100644 --- a/docfx.json +++ b/docfx.json @@ -57,7 +57,8 @@ "field-keyword.md", "unbound-generic-types-in-nameof.md", "first-class-span-types.md", - "simple-lambda-parameters-with-modifiers.md" + "simple-lambda-parameters-with-modifiers.md", + "partial-events-and-constructors.md" ], "src": "_csharplang/proposals", "dest": "csharp/language-reference/proposals", @@ -508,7 +509,7 @@ "_csharplang/proposals/csharp-11.0/*.md": "09/30/2022", "_csharplang/proposals/csharp-12.0/*.md": "08/15/2023", "_csharplang/proposals/csharp-13.0/*.md": "10/31/2024", - "_csharplang/proposals/*.md": "02/14/2025", + "_csharplang/proposals/*.md": "03/11/2025", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "11/08/2022", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "11/08/2023", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "11/09/2024", @@ -686,7 +687,8 @@ "_csharplang/proposals/field-keyword.md": "The `field` contextual keyword", "_csharplang/proposals/unbound-generic-types-in-nameof.md": "Unbound generic types in `nameof`", "_csharplang/proposals/first-class-span-types.md": "First-class span types", - "_csharplang/proposals/simple-lambda-parameters-with-modifiers.md": "Simple lambda parameters with modifiers", + "_csharplang/proposals/simple-lambda-parameters-with-modifiers.md": "Simple lambda parameters with modifiers", + "_csharplang/proposals/partial-events-and-constructors.md": "Partial events and constructors", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "C# compiler breaking changes since C# 10", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "C# compiler breaking changes since C# 11", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "C# compiler breaking changes since C# 12", @@ -811,6 +813,7 @@ "_csharplang/proposals/unbound-generic-types-in-nameof.md": "This proposal introduces the ability to use unbound generic types such as `List<>` in `nameof` expressions. The type argument isn't required.", "_csharplang/proposals/first-class-span-types.md": "This proposal provides several implicit conversions to `Span` and `ReadOnlySpan` that enable library authors to have fewer overloads and developers to write code that resolves to faster Span based APIs", "_csharplang/proposals/simple-lambda-parameters-with-modifiers.md": "This proposal provides allows lambda parmaeters to be declared with modifiers without requiring their type names. You can add modifiers like `ref` and `out` to lambda parameters without specifying their type.", + "_csharplang/proposals/partial-events-and-constructors.md": "This proposal provides allows partial events and constructors to be declared in partial classes. This allows the event and constructor to be split across class declarations.", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "Learn about any breaking changes since the initial release of C# 10 and included in C# 11", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "Learn about any breaking changes since the initial release of C# 11 and included in C# 12", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "Learn about any breaking changes since the initial release of C# 12 and included in C# 13", diff --git a/docs/csharp/event-pattern.md b/docs/csharp/event-pattern.md index 5e88b53485b65..0116f1326c01a 100644 --- a/docs/csharp/event-pattern.md +++ b/docs/csharp/event-pattern.md @@ -22,9 +22,9 @@ void EventRaised(object sender, EventArgs args); This standard signature provides insight into when events are used: -- ***The return type is void***. Events may have zero to many listeners. Raising an event notifies all listeners. In general, listeners don't provide values in response to events. +- ***The return type is void***. Events can have zero to many listeners. Raising an event notifies all listeners. In general, listeners don't provide values in response to events. - ***Events indicate the sender***: The event signature includes the object that raised the event. That provides any listener with a mechanism to communicate with the sender. The compile-time type of `sender` is `System.Object`, even though you likely know a more derived type that would always be correct. By convention, use `object`. -- ***Events package additional information in a single structure***: The `args` parameter is a type derived from that includes any additional necessary information. (You'll see in the [next section](modern-events.md) that this convention is no longer enforced.) If your event type doesn't need any more arguments, you still must provide both arguments. There's a special value, that you should use to denote that your event doesn't contain any additional information. +- ***Events package more information in a single structure***: The `args` parameter is a type derived from that includes any more necessary information. (You'll see in the [next section](modern-events.md) that this convention is no longer enforced.) If your event type doesn't need any more arguments, you still must provide both arguments. There's a special value, that you should use to denote that your event doesn't contain any additional information. Let's build a class that lists files in a directory, or any of its subdirectories that follow a pattern. This component raises an event for each file found that matches the pattern. @@ -48,22 +48,21 @@ The simplest way to add an event to your class is to declare that event as a pub :::code language="csharp" source="./snippets/events/Program.cs" id="DeclareEvent"::: -This looks like it's declaring a public field, which would appear to be a bad object-oriented practice. You want to protect data access through properties, or methods. While this code might look like a bad practice, the code generated by the compiler does create wrappers so that the event objects can only be accessed in safe ways. The only operations available on a field-like event are *add handler*: +This looks like it's declaring a public field, which would appear to be a bad object-oriented practice. You want to protect data access through properties, or methods. While this code might look like a bad practice, the code generated by the compiler does create wrappers so that the event objects can only be accessed in safe ways. The only operations available on a field-like event are *add* and *remove* handler: :::code language="csharp" source="./snippets/events/Program.cs" id="AttachEventHandler"::: -And *remove handler*: - :::code language="csharp" source="./snippets/events/Program.cs" id="DetachHandler"::: -There's a local variable for the handler. If you used the body of the lambda, the remove wouldn't work correctly. It would be a different instance of the delegate, and silently do nothing. +There's a local variable for the handler. If you used the body of the lambda, the `remove` handler wouldn't work correctly. It would be a different instance of the delegate, and silently do nothing. Code outside the class can't raise the event, nor can it perform any other operations. +Beginning with C# 14, events can be declared as [partial members](./language-reference/keywords/partial-member.md). A partial event declaration must include a *defining declaration* and an *implementing declaration*. The defining declaration must use the field-like event syntax. The implementing declaration must declare the `add` and `remove` handlers. + ## Return values from event subscribers -Your simple version is working fine. Let's add another feature: -Cancellation. +Your simple version is working fine. Let's add another feature: Cancellation. When you raise the *Found* event, listeners should be able to stop further processing, if this file is the last one sought. diff --git a/docs/csharp/language-reference/keywords/add.md b/docs/csharp/language-reference/keywords/add.md index 915a3aa8ce8ec..8c30022d01000 100644 --- a/docs/csharp/language-reference/keywords/add.md +++ b/docs/csharp/language-reference/keywords/add.md @@ -1,25 +1,22 @@ --- -description: Learn how to create custom event accessors using add keyword in C# -title: "add keyword" -ms.date: 07/20/2015 +description: The `add` contextual keyword declares an event accessor that adds a handler to that event. +title: "The `add` keyword" +ms.date: 03/13/2025 f1_keywords: - "add_CSharpKeyword" helpviewer_keywords: - "add event accessor [C#]" -ms.assetid: faf30b99-10e8-45cd-ab9a-57585d4d1d8d --- -# add (C# Reference) +# The `add` contextual keyword (C# Reference) -The `add` contextual keyword is used to define a custom event accessor that is invoked when client code subscribes to your [event](./event.md). If you supply a custom `add` accessor, you must also supply a [remove](./remove.md) accessor. - -## Example +The `add` contextual keyword is used to define a custom event accessor that is invoked when client code subscribes to your [event](./event.md). If you supply a custom `add` accessor, you must also supply a [remove](./remove.md) accessor. The following example shows an event that has custom `add` and [remove](./remove.md) accessors. For the full example, see [How to implement interface events](../../programming-guide/events/how-to-implement-interface-events.md). - -[!code-csharp[csrefKeywordsContextual#15](~/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsContextual/CS/csrefKeywordsContextual.cs#15)] - - You do not typically need to provide your own custom event accessors. The accessors that are automatically generated by the compiler when you declare an event are sufficient for most scenarios. - + +:::code language="csharp" source="./snippets/events.cs" id="AddHandler"::: + +You don't typically need to provide your own custom event accessors. The automatically generated accessors when you declare an event are sufficient for most scenarios. Beginning with C# 14, you can declare [`partial`](./partial-member.md) events. The implementing declaration of a partial event must declare the `add` and `remove` handlers. + ## See also - [Events](../../programming-guide/events/index.md) diff --git a/docs/csharp/language-reference/keywords/event.md b/docs/csharp/language-reference/keywords/event.md index 702560cc1888c..f0d6f77815670 100644 --- a/docs/csharp/language-reference/keywords/event.md +++ b/docs/csharp/language-reference/keywords/event.md @@ -1,7 +1,7 @@ --- -description: "event - C# Reference" -title: "event keyword" -ms.date: 07/20/2015 +description: "Learn how to declare events with the `event` keyword - C# Reference" +title: "The `event` keyword" +ms.date: 03/13/2025 f1_keywords: - "event" - "remove" @@ -9,36 +9,35 @@ f1_keywords: - "add" helpviewer_keywords: - "event keyword [C#]" -ms.assetid: 7858fd85-153b-4259-85d0-6aa13c35f174 --- -# event (C# reference) +# The `event` keyword (C# reference) An ***event*** is a member that enables an object to trigger notifications. Event users can attach executable code for events by supplying ***event handlers***. The `event` keyword declares an ***event***. The event is of a delegate type. While an object triggers an event, the event invokes all supplied event handlers. Event handlers are delegate instances added to the event and executed when the event is raised. Event users can add or remove their event handlers on an event. -## Example +The following example shows how to declare and raise an event that uses as the underlying delegate type. For the complete code example, see [How to publish events that conform to .NET Guidelines](/dotnet/standard/events). That sample demonstrates the generic delegate type, how to subscribe to an event, and create an event handler method, -The following example shows how to declare and raise an event that uses as the underlying delegate type. For the complete code example that also shows how to use the generic delegate type and how to subscribe to an event and create an event handler method, see [How to publish events that conform to .NET Guidelines](/dotnet/standard/events). +:::code language="csharp" source="./snippets/Events.cs" id="EventExample"::: -[!code-csharp[csrefKeywordsModifiers#7](~/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsModifiers/CS/csrefKeywordsModifiers.cs#7)] +Events are multicast delegates that can only be invoked from within the class (or derived classes) or struct where they're declared (the publisher class). If other classes or structs subscribe to the event, their event handler methods are called when the publisher class raises the event. For more information and code examples, see [Events](../../programming-guide/events/index.md) and [Delegates](../../programming-guide/delegates/index.md). -Events are a special kind of multicast delegate that can only be invoked from within the class (or derived classes) or struct where they are declared (the publisher class). If other classes or structs subscribe to the event, their event handler methods will be called when the publisher class raises the event. For more information and code examples, see [Events](../../programming-guide/events/index.md) and [Delegates](../../programming-guide/delegates/index.md). +Events can be marked as [`public`](./public.md), [`private`](./private.md), [`protected`](./protected.md), [`internal`](./internal.md), [`protected internal`](./protected-internal.md), or [`private protected`](./private-protected.md). These access modifiers define how users of the class can access the event. For more information, see [Access Modifiers](../../programming-guide/classes-and-structs/access-modifiers.md). -Events can be marked as [public](./public.md), [private](./private.md), [protected](./protected.md), [internal](./internal.md), [protected internal](./protected-internal.md), or [private protected](./private-protected.md). These access modifiers define how users of the class can access the event. For more information, see [Access Modifiers](../../programming-guide/classes-and-structs/access-modifiers.md). +Beginning with C# 14, events can be [`partial`](./partial-member.md). Partial events have one defining declaration and one implementing declaration. The defining declaration must use the field-like syntax. The implementing declaration must declare the `add` and `remove` handlers. ## Keywords and events The following keywords apply to events. -|Keyword|Description|For more information| -|-------------|-----------------|--------------------------| -|[static](./static.md)|Makes the event available to callers at any time, even if no instance of the class exists.|[Static Classes and Static Class Members](../../programming-guide/classes-and-structs/static-classes-and-static-class-members.md)| -|[virtual](./virtual.md)|Allows derived classes to override the event behavior by using the [override](./override.md) keyword.|[Inheritance](../../fundamentals/object-oriented/inheritance.md)| -|[sealed](./sealed.md)|Specifies that for derived classes it is no longer virtual.|| -|[abstract](./abstract.md)|The compiler will not generate the `add` and `remove` event accessor blocks and therefore derived classes must provide their own implementation.|| +| Keyword | Description | For more information | +|---------------------------|-----------------|----------------------| +| [`static`](./static.md) | Makes the event available to callers at any time, even if no instance of the class exists. | [Static Classes and Static Class Members](../../programming-guide/classes-and-structs/static-classes-and-static-class-members.md) | +| [`virtual`](./virtual.md) | Allows derived classes to override the event behavior by using the [override](./override.md) keyword. | [Inheritance](../../fundamentals/object-oriented/inheritance.md)| +| [`sealed`](./sealed.md) | Specifies that for derived classes it's no longer virtual. | | +| [`abstract`](./abstract.md) | The compiler doesn't generate the `add` and `remove` event accessor blocks and therefore derived classes must provide their own implementation. | | -An event may be declared as a static event by using the [static](./static.md) keyword. This makes the event available to callers at any time, even if no instance of the class exists. For more information, see [Static Classes and Static Class Members](../../programming-guide/classes-and-structs/static-classes-and-static-class-members.md). +An event can be declared as a static event by using the [static](./static.md) keyword. Static events are available to callers at any time, even if no instance of the class exists. For more information, see [Static Classes and Static Class Members](../../programming-guide/classes-and-structs/static-classes-and-static-class-members.md). -An event can be marked as a virtual event by using the [virtual](./virtual.md) keyword. This enables derived classes to override the event behavior by using the [override](./override.md) keyword. For more information, see [Inheritance](../../fundamentals/object-oriented/inheritance.md). An event overriding a virtual event can also be [sealed](./sealed.md), which specifies that for derived classes it is no longer virtual. Lastly, an event can be declared [abstract](./abstract.md), which means that the compiler will not generate the `add` and `remove` event accessor blocks. Therefore derived classes must provide their own implementation. +An event can be marked as a virtual event by using the [`virtual`](./virtual.md) keyword. Derived classes can override the event behavior by using the [`override`](./override.md) keyword. For more information, see [Inheritance](../../fundamentals/object-oriented/inheritance.md). An event overriding a virtual event can also be [`sealed`](./sealed.md), which specifies that for derived classes it's no longer virtual. Lastly, an event can be declared [`abstract`](./abstract.md), which means that the compiler doesn't generate the `add` and `remove` event accessor blocks. Therefore derived classes must provide their own implementation. ## C# language specification diff --git a/docs/csharp/language-reference/keywords/partial-member.md b/docs/csharp/language-reference/keywords/partial-member.md index 2d9a607039824..7556ca6e8ada7 100644 --- a/docs/csharp/language-reference/keywords/partial-member.md +++ b/docs/csharp/language-reference/keywords/partial-member.md @@ -1,7 +1,7 @@ --- description: "Partial members are members that can be declared in one partial type declaration and defined in a separate partial type declaration." title: "Partial members" -ms.date: 08/15/2024 +ms.date: 03/13/2025 f1_keywords: - "partialmethod_CSharpKeyword" helpviewer_keywords: @@ -9,12 +9,12 @@ helpviewer_keywords: --- # Partial member (C# Reference) -A partial member has one *declaring declaration* and often one *implementing declaration*. The *declaring declaration* doesn't include a body. The *implementing declaration* provides the body of the member. Partial members enable class designers to provide member hooks that can be implemented by tooling such as source generators. Partial types and members provide a way for human developers to write part of a type while tools write other parts of the type. If the developer doesn't supply an optional implementing declaration, the compiler can remove the declaring declaration at compile time. The following conditions apply to partial members: +A partial member has one *declaring declaration* and often one *implementing declaration*. The *declaring declaration* doesn't include a body. The *implementing declaration* provides the body of the member. Partial members enable class designers to provide member hooks for tooling such as source generators to implement. Partial types and members provide a way for human developers to write part of a type while tools write other parts of the type. If the developer doesn't supply an optional implementing declaration, the compiler can remove the declaring declaration at compile time. The following conditions apply to partial members: - Declarations must begin with the contextual keyword [partial](../../language-reference/keywords/partial-type.md). - Signatures in both parts of the partial type must match. -The `partial` keyword isn't allowed on constructors, finalizers, overloaded operators, or event declarations. Before C# 13, `partial` wasn't allowed on properties or indexers. +The `partial` keyword isn't allowed on static constructors, finalizers, or overloaded operators. Before C# 14, `partial` wasn't allowed on instance constructors or event declarations. Before C# 13, `partial` wasn't allowed on properties or indexers. A partial method isn't required to have an implementing declaration in the following cases: @@ -23,12 +23,14 @@ A partial method isn't required to have an implementing declaration in the follo - It doesn't have any [`out`](method-parameters.md#out-parameter-modifier) parameters. - It doesn't have any of the following modifiers [`virtual`](../../language-reference/keywords/virtual.md), [`override`](../../language-reference/keywords/override.md), [`sealed`](../../language-reference/keywords/sealed.md), [`new`](../../language-reference/keywords/new-modifier.md), or [`extern`](../../language-reference/keywords/extern.md). -Any member that doesn't conform to all those restrictions (for example, `public virtual partial void` method), must provide an implementation. Partial properties and indexers must have an implementation. - The following example shows a partial method that conforms to the preceding restrictions: :::code language="csharp" source="./snippets/PartialMembers.cs" id="OptionalPartialMethod"::: +Any member that doesn't conform to all those restrictions (for example, `public virtual partial void` method), must provide an implementation. + +Partial properties, indexers, and events can't use auto-implemented syntax for the implementing declaration. The defining declaration uses the same syntax. The implementing declaration must include at least one implemented accessor. That accessor can be a [field-backed property](./field.md). The implementing declaration for a partial event must define the `add` and `remove` handlers. + Partial members can also be useful in combination with source generators. For example a regex could be defined using the following pattern: :::code language="csharp" source="./snippets/PartialMembers.cs" id="SourceGeneratorPartial"::: diff --git a/docs/csharp/language-reference/keywords/partial-type.md b/docs/csharp/language-reference/keywords/partial-type.md index 187d29d2915e5..51617b1b21ff0 100644 --- a/docs/csharp/language-reference/keywords/partial-type.md +++ b/docs/csharp/language-reference/keywords/partial-type.md @@ -1,7 +1,7 @@ --- description: "A partial type is a type declaration that allows you to split the declaration of the type into multiple files." title: "Partial type" -ms.date: 08/15/2024 +ms.date: 03/13/2025 f1_keywords: - "partialtype" - "partialtype_CSharpKeyword" @@ -18,9 +18,9 @@ The other declaration contains the implementation of the partial members: :::code language="csharp" source="./snippets/PartialMembers.cs" id="ImplementingPart"::: -The declarations for a partial type can appear in either the same or multiple files. Typically, the two declarations are in different files. You split a class, struct, or interface type when you're working with large projects, or with automatically generated code such as that provided by the [Windows Forms Designer](/dotnet/desktop/winforms/controls/developing-windows-forms-controls-at-design-time) or [Source generators like RegEx](../../../standard/base-types/regular-expression-source-generators.md). A partial type can contain [partial members](partial-member.md). +The declarations for a partial type can appear in either the same or multiple files. Typically, the two declarations are in different files. You split a class, struct, or interface type when you're working with large projects, with automatically generated code such as that provided by the [Windows Forms Designer](/dotnet/desktop/winforms/controls/developing-windows-forms-controls-at-design-time), or [Source generators like RegEx](../../../standard/base-types/regular-expression-source-generators.md). A partial type can contain [partial members](partial-member.md). -Beginning with C# 13, you can define partial properties and partial indexers. Before C# 13, only methods could be defined as partial members. +Beginning with C# 13, you can define partial properties and partial indexers. Beginning with C# 14, you can define partial instance constructors and partial events. Before C# 13, only methods could be defined as partial members. Documentation comments can be provided on either the declaring declaration or the implementing declaration. When documentation comments are applied to both type declarations, the XML elements from each declaration are included in the output XML. See the article on [partial members](./partial-member.md) for the rules on partial member declarations. diff --git a/docs/csharp/language-reference/keywords/remove.md b/docs/csharp/language-reference/keywords/remove.md index 7d51ed57bfa6f..b432d7f8b142d 100644 --- a/docs/csharp/language-reference/keywords/remove.md +++ b/docs/csharp/language-reference/keywords/remove.md @@ -1,24 +1,21 @@ --- -description: "remove contextual keyword - C# Reference" -title: "remove contextual keyword" -ms.date: 07/20/2015 +description: "The `remove` contextual keyword declares an event accessor that removes a handler from that event. - C# Reference" +title: "The `remove` contextual keyword" +ms.date: 03/13/2025 f1_keywords: - "remove_CSharpKeyword" helpviewer_keywords: - "remove event accessor [C#]" -ms.assetid: c8223426-c17b-4fe2-8406-01564cf1dd2b --- -# remove (C# Reference) +# The `remove` contextual keyword (C# Reference) The `remove` contextual keyword is used to define a custom event accessor that is invoked when client code unsubscribes from your [event](event.md). If you supply a custom `remove` accessor, you must also supply an [add](add.md) accessor. -## Example - The following example shows an event with custom [add](add.md) and `remove` accessors. For the full example, see [How to implement interface events](../../programming-guide/events/how-to-implement-interface-events.md). - [!code-csharp[csrefKeywordsContextual#15](~/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsContextual/CS/csrefKeywordsContextual.cs#15)] +:::code language="csharp" source="./snippets/events.cs" id="AddHandler"::: -You do not typically need to provide your own custom event accessors. The accessors that are automatically generated by the compiler when you declare an event are sufficient for most scenarios. +You don't typically need to provide your own custom event accessors. The automatically generated accessors when you declare an event are sufficient for most scenarios. Beginning with C# 14, you can declare [`partial`](./partial-member.md) events. The implementing declaration of a partial event must declare the `add` and `remove` handlers. ## See also diff --git a/docs/csharp/language-reference/keywords/snippets/events.cs b/docs/csharp/language-reference/keywords/snippets/events.cs new file mode 100644 index 0000000000000..7ab33ea1147cb --- /dev/null +++ b/docs/csharp/language-reference/keywords/snippets/events.cs @@ -0,0 +1,45 @@ +namespace Events; + +// +public class SampleEventArgs +{ + public SampleEventArgs(string text) { Text = text; } + public string Text { get; } // readonly +} + +public class Publisher +{ + // Declare the delegate (if using non-generic pattern). + public delegate void SampleEventHandler(object sender, SampleEventArgs e); + + // Declare the event. + public event SampleEventHandler SampleEvent; + + // Wrap the event in a protected virtual method + // to enable derived classes to raise the event. + protected virtual void RaiseSampleEvent() + { + // Raise the event in a thread-safe manner using the ?. operator. + SampleEvent?.Invoke(this, new SampleEventArgs("Hello")); + } +} +// + +interface IDrawingObject +{ + event EventHandler OnDraw; +} + +// +class Events : IDrawingObject +{ + event EventHandler PreDrawEvent; + + event EventHandler IDrawingObject.OnDraw + { + add => PreDrawEvent += value; + remove => PreDrawEvent -= value; + } +} +// + diff --git a/docs/csharp/programming-guide/classes-and-structs/constructors.md b/docs/csharp/programming-guide/classes-and-structs/constructors.md index 88c8232459985..770a31d9aac98 100644 --- a/docs/csharp/programming-guide/classes-and-structs/constructors.md +++ b/docs/csharp/programming-guide/classes-and-structs/constructors.md @@ -1,7 +1,7 @@ --- title: "Constructors" description: A constructor in C# is called when a class or struct is created. Use constructors to set defaults, limit instantiation, and write flexible, easy-to-read code. -ms.date: 01/15/2025 +ms.date: 03/11/2025 helpviewer_keywords: - "constructors [C#]" - "classes [C#], constructors" @@ -24,13 +24,15 @@ The preceding actions take place when an instance is created using the [`new` op The [static constructor](static-constructors.md), if any, runs before any of the instance constructor actions take place for any instance of the type. The static constructor runs at most once. +Beginning with C# 14, you can declare instance constructors as [partial members](./partial-classes-and-methods.md). [Partial constructors](#partial-constructors) must have both a declaring and implementing declaration. + ## Constructor syntax A constructor is a method with the same name as its type. Its method signature can include an optional [access modifier](./access-modifiers.md), the method name, and its parameter list; it doesn't include a return type. The following example shows the constructor for a class named `Person`. :::code source="./snippets/constructors/Program.cs" id="InstanceCtor"::: -If a constructor can be implemented as a single statement, you can use an [expression body member](../statements-expressions-operators/expression-bodied-members.md). The following example defines a `Location` class whose constructor has a single string parameter named *name*. The expression body definition assigns the argument to the `locationName` field. +If a constructor can be implemented as a single statement, you can use an [expression body member](../statements-expressions-operators/expression-bodied-members.md). The following example defines a `Location` class whose constructor has a single string parameter, `name`. The expression body definition assigns the argument to the `locationName` field. :::code source="./snippets/constructors/Program.cs" id="ExpressionBodiedCtor"::: @@ -38,6 +40,8 @@ If a type requires a parameter to create an instance, you can use a *primary con :::code source="./snippets/constructors/Program.cs" id="PrimaryCtor"::: +You can declare a primary constructor on a `partial` type. Only one primary constructor declaration is allowed. In other words, only one declaration of the `partial` type can include the parameters for the primary constructor. + ## Static constructors The previous examples show instance constructors, which initialize a new object. A class or struct can also declare a static constructor, which initializes static members of the type. Static constructors are parameterless. If you don't provide a static constructor to initialize static fields, the C# compiler initializes static fields to their default value as listed in the [Default values of C# types](../../language-reference/builtin-types/default-values.md) article. @@ -52,6 +56,10 @@ You can also define a static constructor with an expression body definition, as For more information and examples, see [Static Constructors](./static-constructors.md). +## Partial constructors + +Beginning with C# 14, you can declare *partial constructors* in a partial class or struct. Any partial constructor must have a *defining declaration* and an *implementing declaration*. The signatures of the declaring and implementing partial constructors must match according to the rules of [partial members](./partial-classes-and-methods.md#partial-members). The defining declaration of the partial constructor can't use the `: base()` or `: this()` constructor initializer. You add any constructor initializer must be added on the implementing declaration. + ## See also - [The C# type system](../../fundamentals/types/index.md) diff --git a/docs/csharp/programming-guide/classes-and-structs/partial-classes-and-methods.md b/docs/csharp/programming-guide/classes-and-structs/partial-classes-and-methods.md index 453df520a446a..4598d32d1c20d 100644 --- a/docs/csharp/programming-guide/classes-and-structs/partial-classes-and-methods.md +++ b/docs/csharp/programming-guide/classes-and-structs/partial-classes-and-methods.md @@ -1,15 +1,16 @@ --- -title: "Partial Classes and Methods" -description: Partial classes and methods in C# split the definition of a class, a struct, an interface, or a method over two or more source files. -ms.date: 10/31/2024 +title: "Partial Classes and Members" +description: Partial classes and members in C# split the definition of a class, a struct, an interface, or a member over two or more source files. +ms.date: 03/11/2025 helpviewer_keywords: - "partial methods [C#]" + - "partial members [C#]" - "partial classes [C#]" - "C# language, partial classes and methods" --- -# Partial Classes and Methods (C# Programming Guide) +# Partial Classes and Members (C# Programming Guide) -It's possible to split the definition of a [class](../../language-reference/keywords/class.md), a [struct](../../language-reference/builtin-types/struct.md), an [interface](../../language-reference/keywords/interface.md), or a method over two or more source files. Each source file contains a section of the type or method definition, and all parts are combined when the application is compiled. +It's possible to split the definition of a [class](../../language-reference/keywords/class.md), a [struct](../../language-reference/builtin-types/struct.md), an [interface](../../language-reference/keywords/interface.md), or a member over two or more source files. Each source file contains a section of the type or member definition, and all parts are combined when the application is compiled. ## Partial Classes @@ -32,7 +33,7 @@ If any part is declared abstract, then the whole type is considered abstract. If All the parts that specify a base class must agree, but parts that omit a base class still inherit the base type. Parts can specify different base interfaces, and the final type implements all the interfaces listed by all the partial declarations. Any class, struct, or interface members declared in a partial definition are available to all the other parts. The final type is the combination of all the parts at compile time. > [!NOTE] -> The `partial` modifier is not available on delegate or enumeration declarations. +> The `partial` modifier isn't available on delegate or enumeration declarations. The following example shows that nested types can be partial, even if the type they're nested within isn't partial itself. @@ -114,9 +115,9 @@ An implementation isn't required for a partial method when the signature obeys t The method and all calls to the method are removed at compile time when there's no implementation. -Any method that doesn't conform to all those restrictions, including properties and indexers, must provide an implementation. That implementation might be supplied by a *source generator*. [Partial properties](../../language-reference/keywords/partial-member.md) can't be implemented using automatically implemented properties. The compiler can't distinguish between an automatically implemented property, and the declaring declaration of a partial property. +Any member that doesn't conform to all those restrictions, including constructors, properties, indexers, and events, must provide an implementation. That implementation might be supplied by a *source generator*. [Partial properties](../../language-reference/keywords/partial-member.md) can't be implemented using automatically implemented properties. The compiler can't distinguish between an automatically implemented property, and the declaring declaration of a partial property. -Beginning with C# 13, the implementing declaration for a partial property can use [field backed properties](../../language-reference/keywords/field.md) to define the implementing declaration. A field backed property provides a concise syntax where the `field` keyword accesses the compiler synthesized backing field for the property. For example, you could write the following: +Beginning with C# 13, the implementing declaration for a partial property can use [field backed properties](../../language-reference/keywords/field.md) to define the implementing declaration. A field backed property provides a concise syntax where the `field` keyword accesses the compiler synthesized backing field for the property. For example, you could write the following code: :::code language="csharp" source="snippets/partial-classes-and-methods/Program.cs" id="FieldProperty"::: @@ -124,7 +125,7 @@ You can use `field` in either the `get` or `set` accessor, or both. [!INCLUDE[field-preview](../../includes/field-preview.md)] -Partial methods enable the implementer of one part of a class to declare a member. The implementer of another part of the class can define that member. There are two scenarios where this separation is useful: templates that generate boilerplate code, and source generators. +Partial members enable the implementer of one part of a class to declare a member. The implementer of another part of the class can define that member. There are two scenarios where this separation is useful: templates that generate boilerplate code, and source generators. - **Template code**: The template reserves a method name and signature so that generated code can call the method. These methods follow the restrictions that enable a developer to decide whether to implement the method. If the method isn't 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 isn't supplied. No compile-time or run-time errors result if the method is called but not implemented. - **Source generators**: Source generators provide an implementation for members. The human developer can add the member declaration (often with attributes read by the source generator). The developer can write code that calls these members. The source generator runs during compilation and provides the implementation. In this scenario, the restrictions for partial members that might not be implemented often aren't followed. @@ -143,12 +144,12 @@ partial void OnNameChanged() - Partial member declarations must begin with the contextual keyword [partial](../../language-reference/keywords/partial-type.md). - Partial member signatures in both parts of the partial type must match. - Partial member can have [static](../../language-reference/keywords/static.md) and [unsafe](../../language-reference/keywords/unsafe.md) modifiers. -- Partial member can be generic. Constraints must be the same on the defining and implementing method declaration. Parameter and type parameter names don't have to be the same in the implementing declaration as in the defining one. +- Partial member can be generic. Constraints must be the same on the defining and implementing member declaration. Parameter and type parameter names don't have to be the same in the implementing declaration as in the defining one. - You can make a [delegate](../../language-reference/builtin-types/reference-types.md) to a partial method defined and implemented, but not to a partial method that doesn't have an implementation. ## C# Language Specification -For more information, see [Partial types](~/_csharpstandard/standard/classes.md#1527-partial-type-declarations) and [Partial methods](~/_csharpstandard/standard/classes.md#1569-partial-methods) in the [C# Language Specification](~/_csharpstandard/standard/README.md). The language specification is the definitive source for C# syntax and usage. The new features for partial methods are defined in the [feature specification](~/_csharplang/proposals/csharp-9.0/extending-partial-methods.md). +For more information, see [Partial types](~/_csharpstandard/standard/classes.md#1527-partial-type-declarations) and [Partial methods](~/_csharpstandard/standard/classes.md#1569-partial-methods) in the [C# Language Specification](~/_csharpstandard/standard/README.md). The language specification is the definitive source for C# syntax and usage. The new features for partial members are defined in the feature specifications for [extending partial methods](~/_csharplang/proposals/csharp-9.0/extending-partial-methods.md), [partial properties and indexers](~/_csharplang/proposals/csharp-13.0/partial-properties.md), and [partial events and constructors](~/_csharplang/proposals/partial-events-and-constructors.md). ## See also diff --git a/docs/csharp/programming-guide/events/index.md b/docs/csharp/programming-guide/events/index.md index 6f18cccb65f5c..448574f9efdf7 100644 --- a/docs/csharp/programming-guide/events/index.md +++ b/docs/csharp/programming-guide/events/index.md @@ -1,53 +1,43 @@ --- title: "Events" description: Learn about events. Events enable a class or object to notify other classes or objects when something of interest occurs. -ms.date: 07/20/2015 +ms.date: 03/11/2025 helpviewer_keywords: - "classes [C#], events" - "C# language, events" - "events [C#]" -ms.assetid: a8e51b22-d294-44fb-9539-0072f06c4cb3 --- # Events (C# Programming Guide) -Events enable a [class](../../language-reference/keywords/class.md) or object to notify other classes or objects when something of interest occurs. The class that sends (or *raises*) the event is called the *publisher* and the classes that receive (or *handle*) the event are called *subscribers*. - +Events enable a [class](../../language-reference/keywords/class.md) or object to notify other classes or objects when something of interest occurs. The class that sends (or *raises*) the event is called the *publisher* and the classes that receive (or *handle*) the event are called *subscribers*. + In a typical C# Windows Forms or Web application, you subscribe to events raised by controls such as buttons and list boxes. You can use the Visual C# integrated development environment (IDE) to browse the events that a control publishes and select the ones that you want to handle. The IDE provides an easy way to automatically add an empty event handler method and the code to subscribe to the event. For more information, see [How to subscribe to and unsubscribe from events](./how-to-subscribe-to-and-unsubscribe-from-events.md). - -## Events Overview - - Events have the following properties: - -- The publisher determines when an event is raised; the subscribers determine what action is taken in response to the event. - -- An event can have multiple subscribers. A subscriber can handle multiple events from multiple publishers. - -- Events that have no subscribers are never raised. - -- Events are typically used to signal user actions such as button clicks or menu selections in graphical user interfaces. - -- When an event has multiple subscribers, the event handlers are invoked synchronously when an event is raised. To invoke events asynchronously, see [Calling Synchronous Methods Asynchronously](../../../standard/asynchronous-programming-patterns/calling-synchronous-methods-asynchronously.md). - -- In the .NET class library, events are based on the delegate and the base class. - -## Related Sections - - For more information, see: - -- [How to subscribe to and unsubscribe from events](./how-to-subscribe-to-and-unsubscribe-from-events.md) -- [How to publish events that conform to .NET Guidelines](/dotnet/standard/events) +## Events Overview -- [How to raise base class events in derived classes](./how-to-raise-base-class-events-in-derived-classes.md) +Events have the following properties: -- [How to implement interface events](./how-to-implement-interface-events.md) +- The publisher determines when an event is raised; the subscribers determine what action is taken in response to the event. +- An event can have multiple subscribers. A subscriber can handle multiple events from multiple publishers. +- Events that have no subscribers are never raised. +- Events are typically used to signal user actions such as button clicks or menu selections in graphical user interfaces. +- When an event has multiple subscribers, the event handlers are invoked synchronously when an event is raised. To invoke events asynchronously, see [Calling Synchronous Methods Asynchronously](../../../standard/asynchronous-programming-patterns/calling-synchronous-methods-asynchronously.md). +- In the .NET class library, events are based on the delegate and the base class. + +## Related Sections + For more information, see: + +- [How to subscribe to and unsubscribe from events](./how-to-subscribe-to-and-unsubscribe-from-events.md) +- [How to publish events that conform to .NET Guidelines](/dotnet/standard/events) +- [How to raise base class events in derived classes](./how-to-raise-base-class-events-in-derived-classes.md) +- [How to implement interface events](./how-to-implement-interface-events.md) - [How to implement custom event accessors](./how-to-implement-custom-event-accessors.md) -## C# Language Specification +## C# Language Specification For more information, see [Events](~/_csharpstandard/standard/classes.md#158-events) in the [C# Language Specification](~/_csharpstandard/standard/README.md). The language specification is the definitive source for C# syntax and usage. - + ## See also - diff --git a/docs/csharp/specification/toc.yml b/docs/csharp/specification/toc.yml index 56dbe3ff9267f..bcc71fe59011e 100644 --- a/docs/csharp/specification/toc.yml +++ b/docs/csharp/specification/toc.yml @@ -201,6 +201,8 @@ items: href: ../../../_csharplang/proposals/csharp-9.0/extending-partial-methods.md - name: Partial properties href: ../../../_csharplang/proposals/csharp-13.0/partial-properties.md + - name: Partial events and constructors + href: ../../../_csharplang/proposals/partial-events-and-constructors.md - name: Field backed properties (preview) href: ../../../_csharplang/proposals/field-keyword.md - name: Covariant return types diff --git a/docs/csharp/whats-new/csharp-14.md b/docs/csharp/whats-new/csharp-14.md index 21c15e42a8726..ad68a7a62985f 100644 --- a/docs/csharp/whats-new/csharp-14.md +++ b/docs/csharp/whats-new/csharp-14.md @@ -12,6 +12,7 @@ C# 14 includes the following new features. You can try these features using the - [More implicit conversions for `Span` and `ReadOnlySpan`](#implicit-span-conversions) - [Modifiers on simple lambda parameters](#simple-lambda-parameters-with-modifiers) - [`field` backed properties](#the-field-keyword) +- [`partial` events and constructors](#more-partial-members) C# 14 is supported on **.NET 10**. For more information, see [C# language versioning](../language-reference/configure-language-version.md). @@ -50,11 +51,11 @@ public string Message You can declare a body for one or both accessors for a field backed property. -There's a potential breaking change or confusion reading code in types that also include a symbol named `field`. You can use `@field` or `this.field` to disambiguate between the `field` keyword and the identifier, or you can rename the current `field` symbol to provide more distinction. +There's a potential breaking change or confusion reading code in types that also include a symbol named `field`. You can use `@field` or `this.field` to disambiguate between the `field` keyword and the identifier, or you can rename the current `field` symbol to provide better distinction. If you try this feature and have feedback, comment on the [feature issue](https://github.com/dotnet/csharplang/issues/140) in the `csharplang` repository. -The [`field`](../language-reference/keywords/field.md) contextual keyword is in C# 13 as a preview feature. You can try it if you're using .NET 9 and C# 13 to provide [feedback](https://github.com/dotnet/csharplang/issues/140). +The [`field`](../language-reference/keywords/field.md) contextual keyword is in C# 13 as a preview feature. ## Implicit span conversions @@ -64,13 +65,13 @@ C# 14 introduces first-class support for )` evaluates to `List`. In earlier versions of C#, only closed generic types, such as `List`, could be used to return the `List` name. ## Simple lambda parameters with modifiers -Beginning with C# 14, you can add parameter modifiers, such as `scoped`, `ref`, `in`, `out`, or `ref readonly` to lambda expression parameters without specifying the parameter type: +You can add parameter modifiers, such as `scoped`, `ref`, `in`, `out`, or `ref readonly` to lambda expression parameters without specifying the parameter type: ```csharp delegate bool TryParse(string text, out T result); @@ -88,6 +89,16 @@ The `params` modifier still requires an explicitly typed parameter list. You can read more about these changes in the article on [lambda expressions](../language-reference/operators/lambda-expressions.md#input-parameters-of-a-lambda-expression) in the C# language reference. +## More partial members + +You can now declare [instance constructors](../programming-guide/classes-and-structs/constructors.md#partial-constructors) and [events](../event-pattern.md) as [partial members](../language-reference/keywords/partial-member.md). + +Partial constructors and partial events must include exactly one *defining declaration* and one *implementing declaration*. + +Only the implementing declaration of a partial constructor can include a constructor initializer: `this()` or `base()`. Only one partial type declaration can include the primary constructor syntax. + +The implementing declaration of a partial event must include `get` and `remove` accessors. The defining declaration declares a field-like event. + ## See also - [What's new in .NET 10](../../core/whats-new/dotnet-10/overview.md) diff --git a/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsContextual/CS/csrefKeywordsContextual.cs b/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsContextual/CS/csrefKeywordsContextual.cs index 75314e646778f..9e5a0f85bf488 100644 --- a/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsContextual/CS/csrefKeywordsContextual.cs +++ b/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsContextual/CS/csrefKeywordsContextual.cs @@ -78,21 +78,4 @@ join prod in products on category.ID equals prod.CategoryID } } - interface IDrawingObject - { - event EventHandler OnDraw; - } - - // - class Events : IDrawingObject - { - event EventHandler PreDrawEvent; - - event EventHandler IDrawingObject.OnDraw - { - add => PreDrawEvent += value; - remove => PreDrawEvent -= value; - } - } - // } diff --git a/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsModifiers/CS/csrefKeywordsModifiers.cs b/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsModifiers/CS/csrefKeywordsModifiers.cs index 96345027ae440..3204553d3476b 100644 --- a/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsModifiers/CS/csrefKeywordsModifiers.cs +++ b/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsModifiers/CS/csrefKeywordsModifiers.cs @@ -214,31 +214,6 @@ static void Main() // Output: My local constant = 707 // - // - public class SampleEventArgs - { - public SampleEventArgs(string text) { Text = text; } - public string Text { get; } // readonly - } - - public class Publisher - { - // Declare the delegate (if using non-generic pattern). - public delegate void SampleEventHandler(object sender, SampleEventArgs e); - - // Declare the event. - public event SampleEventHandler SampleEvent; - - // Wrap the event in a protected virtual method - // to enable derived classes to raise the event. - protected virtual void RaiseSampleEvent() - { - // Raise the event in a thread-safe manner using the ?. operator. - SampleEvent?.Invoke(this, new SampleEventArgs("Hello")); - } - } - // - // //using System.Runtime.InteropServices; class ExternTest