Skip to content

Commit a9fcef5

Browse files
committed
Further edits for extensions
1 parent 401f71d commit a9fcef5

File tree

12 files changed

+98
-297
lines changed

12 files changed

+98
-297
lines changed

docs/csharp/fundamentals/object-oriented/index.md

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "Classes, structs, and records"
33
description: Describes the use of classes, structures (structs), and records in C#.
4-
ms.date: 03/23/2022
4+
ms.date: 04/11/2025
55
helpviewer_keywords:
66
- "structs [C#], about structs"
77
- "records [C#], about records"
@@ -14,17 +14,17 @@ helpviewer_keywords:
1414
---
1515
# Overview of object oriented techniques in C\#
1616

17-
In C#, the definition of a type—a class, struct, or record—is like a blueprint that specifies what the type can do. An object is basically a block of memory that has been allocated and configured according to the blueprint. This article provides an overview of these blueprints and their features. The [next article in this series](objects.md) introduces objects.
17+
In C#, the definition of a type—a class, struct, or record—is like a blueprint that specifies what the type can do. An object is basically a block of memory allocated and configured according to the blueprint. This article provides an overview of these blueprints and their features. The [next article in this series](objects.md) introduces objects.
1818

1919
## Encapsulation
2020

21-
*Encapsulation* is sometimes referred to as the first pillar or principle of object-oriented programming. A class or struct can specify how accessible each of its members is to code outside of the class or struct. Methods and variables that aren't intended to be used from outside of the class or assembly can be hidden to limit the potential for coding errors or malicious exploits. For more information, see the [Object-oriented programming](../tutorials/oop.md) tutorial.
21+
*Encapsulation* is sometimes referred to as the first pillar or principle of object-oriented programming. A class or struct can specify how accessible each of its members is to code outside of the class or struct. Member not intended for consumers outside of the class or assembly are hidden to limit the potential for coding errors or malicious exploits. For more information, see the [Object-oriented programming](../tutorials/oop.md) tutorial.
2222

2323
## Members
2424

2525
The *members* of a type include all methods, fields, constants, properties, and events. In C#, there are no global variables or methods as there are in some other languages. Even a program's entry point, the `Main` method, must be declared within a class or struct (implicitly when you use [top-level statements](../program-structure/top-level-statements.md)).
2626

27-
The following list includes all the various kinds of members that may be declared in a class, struct, or record.
27+
The following list includes all the various kinds of members that can be declared in a class, struct, or record.
2828

2929
- Fields
3030
- Constants
@@ -56,7 +56,7 @@ The default accessibility is `private`.
5656

5757
Classes (but not structs) support the concept of inheritance. A class that derives from another class, called the *base class*, automatically contains all the public, protected, and internal members of the base class except its constructors and finalizers.
5858

59-
Classes may be declared as [abstract](../../language-reference/keywords/abstract.md), which means that one or more of their methods have no implementation. Although abstract classes cannot be instantiated directly, they can serve as base classes for other classes that provide the missing implementation. Classes can also be declared as [sealed](../../language-reference/keywords/sealed.md) to prevent other classes from inheriting from them.
59+
Classes can be declared as [abstract](../../language-reference/keywords/abstract.md), which means that one or more of their methods have no implementation. Although abstract classes can't be instantiated directly, they can serve as base classes for other classes that provide the missing implementation. Classes can also be declared as [sealed](../../language-reference/keywords/sealed.md) to prevent other classes from inheriting from them.
6060

6161
For more information, see [Inheritance](./inheritance.md) and [Polymorphism](./polymorphism.md).
6262

@@ -66,7 +66,7 @@ Classes, structs, and records can implement multiple interfaces. To implement fr
6666

6767
## Generic Types
6868

69-
Classes, structs, and records can be defined with one or more type parameters. Client code supplies the type when it creates an instance of the type. For example, the <xref:System.Collections.Generic.List%601> class in the <xref:System.Collections.Generic> namespace is defined with one type parameter. Client code creates an instance of a `List<string>` or `List<int>` to specify the type that the list will hold. For more information, see [Generics](../types/generics.md).
69+
Classes, structs, and records can be defined with one or more type parameters. Client code supplies the type when it creates an instance of the type. For example, the <xref:System.Collections.Generic.List%601> class in the <xref:System.Collections.Generic> namespace is defined with one type parameter. Client code creates an instance of a `List<string>` or `List<int>` to specify the type that the list holds. For more information, see [Generics](../types/generics.md).
7070

7171
## Static Types
7272

@@ -86,9 +86,9 @@ You can instantiate and initialize class or struct objects, and collections of o
8686

8787
## Anonymous Types
8888

89-
In situations where it isn't convenient or necessary to create a named class you use anonymous types. Anonymous types are defined by their named data members. For more information, see [Anonymous types](../types/anonymous-types.md).
89+
In situations where it isn't convenient or necessary to create a named class you use anonymous types. Named data members define anonymous types. For more information, see [Anonymous types](../types/anonymous-types.md).
9090

91-
## Extension Methods
91+
## Extension Members
9292

9393
You can "extend" a class without creating a derived class by creating a separate type. That type contains methods that can be called as if they belonged to the original type. For more information, see [Extension methods](../../programming-guide/classes-and-structs/extension-methods.md).
9494

docs/csharp/language-reference/keywords/base.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "The base keyword"
33
description: Learn about the base keyword, which is used to access members of the base class from within a derived class in C#.
4-
ms.date: 04/08/2025
4+
ms.date: 04/11/2025
55
f1_keywords:
66
- "base"
77
- "BaseClass.BaseClass"

docs/csharp/language-reference/keywords/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
description: "C# Keywords: Find the reference material for the predefined keywords and contextual keywords defined in the C# language."
33
title: "C# Keywords and contextual keywords"
4-
ms.date: 08/14/2024
4+
ms.date: 04/11/2025
55
f1_keywords:
66
- "cs.keywords"
77
helpviewer_keywords:

docs/csharp/language-reference/keywords/snippets/Extensions.cs

+1
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,6 @@ public static void BasicExample()
7272
// <UseStaticExtensions>
7373
var newSequence = IEnumerable<int>.Generate(5, 10, 2);
7474
var identity = IEnumerable<int>.Identity;
75+
// </UseStaticExtensions>
7576
}
7677
}

docs/csharp/language-reference/keywords/this.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "The this keyword"
33
description: The `this` keyword clarifies access to the current instance of a type, or declares an indexer on the type.
4-
ms.date: 04/08/2025
4+
ms.date: 04/11/2025
55
f1_keywords:
66
- "this"
77
- "this_CSharpKeyword"

docs/csharp/linq/how-to-extend-linq.md

+12-55
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
title: "How to: Write your own extensions to LINQ"
33
description: Learn techniques to extend the standard LINQ methods. Query based on runtime state, modify query objects, and extend LINQ capabilities.
44
ms.topic: how-to
5-
ms.date: 04/22/2024
5+
ms.date: 04/11/2025
66
---
77
# How to extend LINQ
88

9-
All LINQ based methods follow one of two similar patterns. They take an enumerable sequence. They return either a different sequence, or a single value. The consistency of the shape enables you to extend LINQ by writing methods with a similar shape. In fact, the .NET libraries have gained new methods in many .NET releases since LINQ was first introduced. In this article, you see examples of extending LINQ by writing your own methods that follow the same pattern.
9+
All LINQ based methods follow one of two similar patterns. They take an enumerable sequence. They return either a different sequence, or a single value. The consistency of the shape enables you to extend LINQ by writing methods with a similar shape. In fact, the .NET libraries gained new methods in many .NET releases since LINQ was first introduced. In this article, you see examples of extending LINQ by writing your own methods that follow the same pattern.
1010

1111
## Add custom methods for LINQ queries
1212

@@ -16,11 +16,15 @@ When you extend the <xref:System.Collections.Generic.IEnumerable%601> interface,
1616

1717
An *aggregate* method computes a single value from a set of values. LINQ provides several aggregate methods, including <xref:System.Linq.Enumerable.Average%2A>, <xref:System.Linq.Enumerable.Min%2A>, and <xref:System.Linq.Enumerable.Max%2A>. You can create your own aggregate method by adding an extension method to the <xref:System.Collections.Generic.IEnumerable%601> interface.
1818

19-
The following code example shows how to create an extension method called `Median` to compute a median for a sequence of numbers of type `double`.
19+
Beginning in C# 14, you can declare an *extension block* to contain multiple extension members. You declare an extension block with the keyword `extension` followed by the receiver parameter in parentheses. The following code example shows how to create an extension method called `Median` in an extension block. The method computes a median for a sequence of numbers of type `double`.
20+
21+
:::code language="csharp" source="./snippets/HowToExtend/ExtensionMembers.cs" ID="MedianExtensionMember":::
22+
23+
You can also add the `this` modifier to a static method to declare an *extension method*. The following code shows the equivalent `Median` extension method:
2024

2125
:::code language="csharp" source="./snippets/HowToExtend/LinqExtensions.cs" ID="LinqExtensionClass":::
2226

23-
You call this extension method for any enumerable collection in the same way you call other aggregate methods from the <xref:System.Collections.Generic.IEnumerable%601> interface.
27+
You call either extension method for any enumerable collection in the same way you call other aggregate methods from the <xref:System.Collections.Generic.IEnumerable%601> interface.
2428

2529
The following code example shows how to use the `Median` method for an array of type `double`.
2630

@@ -58,57 +62,10 @@ You can call this extension method for any enumerable collection just as you wou
5862

5963
:::code language="csharp" source="./snippets/HowToExtend/Program.cs" ID="SequenceUsage":::
6064

61-
## Group results by contiguous keys
62-
63-
The following example shows how to group elements into chunks that represent subsequences of contiguous keys. For example, assume that you're given the following sequence of key-value pairs:
64-
65-
|Key | Value |
66-
|----|--------|
67-
| A | We |
68-
| A | think |
69-
| A | that |
70-
| B | Linq |
71-
| C | is |
72-
| A | really |
73-
| B | cool |
74-
| B | ! |
75-
76-
The following groups are created in this order:
77-
78-
1. We, think, that
79-
1. Linq
80-
1. is
81-
1. really
82-
1. cool, !
83-
84-
The solution is implemented as a thread-safe extension method that returns its results in a streaming manner. It produces its groups as it moves through the source sequence. Unlike the `group` or `orderby` operators, it can begin returning groups to the caller before reading the entire sequence. The following example shows both the extension method and the client code that uses it:
85-
86-
:::code language="csharp" source="./snippets/HowToExtend/GroupByContiguousKeys.cs" id="group_by_contiguous_keys_chunkextensions":::
87-
88-
:::code language="csharp" source="./snippets/HowToExtend/GroupByContiguousKeys.cs" id="group_by_contiguous_keys_client_code":::
89-
90-
### `ChunkExtensions` class
91-
92-
In the presented code of the `ChunkExtensions` class implementation, the `while(true)` loop in the `ChunkBy` method iterates through source sequence and creates a copy of each Chunk. On each pass, the iterator advances to the first element of the next "Chunk", represented by a [`Chunk`](#chunk-class) object, in the source sequence. This loop corresponds to the outer foreach loop that executes the query. In that loop, the code does the following actions:
93-
94-
1. Get the key for the current Chunk and assign it to `key` variable. The source iterator consumes the source sequence until it finds an element with a key that doesn't match.
95-
1. Make a new Chunk (group) object, and store it in `current` variable. It has one GroupItem, a copy of the current source element.
96-
1. Return that Chunk. A Chunk is an `IGrouping<TKey,TSource>`, which is the return value of the [`ChunkBy`](#chunk-class) method. The Chunk only has the first element in its source sequence. The remaining elements are returned only when the client code foreach's over this chunk. See `Chunk.GetEnumerator` for more info.
97-
1. Check to see if:
98-
- The chunk has a copy of all its source elements, or
99-
- The iterator reached the end of the source sequence.
100-
1. When the caller has enumerated all the chunk items, the `Chunk.GetEnumerator` method has copied all chunk items. If the `Chunk.GetEnumerator` loop didn't enumerate all elements in the chunk, do it now to avoid corrupting the iterator for clients that might be calling it on a separate thread.
101-
102-
### `Chunk` class
103-
104-
The `Chunk` class is a contiguous group of one or more source elements that have the same key. A Chunk has a key and a list of ChunkItem objects, which are copies of the elements in the source sequence:
105-
106-
:::code language="csharp" source="./snippets/HowToExtend/GroupByContiguousKeys.cs" id="group_by_contiguous_keys_chunk_class":::
107-
108-
Each `ChunkItem` (represented by `ChunkItem` class) has a reference to the next `ChunkItem` in the list. The list consists of its `head` - which stores the contents of the first source element that belongs with this chunk, and its `tail` - which is an end of the list. The tail is repositioned each time a new `ChunkItem` is added. The tail of the linked list is set to `null` in the `CopyNextChunkElement` method if the key of the next element doesn't match the current chunk's key, or there are no more elements in the source.
65+
Each example shown in this article has a different *receiver*. That means each method must be declared in a different extension block that specifies the unique receiver. The following code example shows a single static class with three different extension blocks, each of which contains one of the methods defined in this article:
10966

110-
The `CopyNextChunkElement` method of the `Chunk` class adds one `ChunkItem` to the current group of items. It tries to advance the iterator on the source sequence. If the `MoveNext()` method returns `false` the iteration is at the end, and `isLastSourceElement` is set to `true`.
67+
:::code language="csharp" source="./snippets/HowToExtend/ExtensionMembers.cs" ID="ExtensionMemberClass":::
11168

112-
The `CopyAllChunkElements` method is called after the end of the last chunk was reached. It checks whether there are more elements in the source sequence. If there are, it returns `true` if the enumerator for this chunk was exhausted. In this method, when the private `DoneCopyingChunk` field is checked for `true`, if isLastSourceElement is `false`, it signals to the outer iterator to continue iterating.
69+
The final extension block declares a generic extension block. The type parameter for the receiver is declared on the `extension` itself.
11370

114-
The inner foreach loop invokes the `GetEnumerator` method of the `Chunk` class. This method stays one element ahead of the client requests. It adds the next element of the chunk only after the client requests the previous last element in the list.
71+
The preceding example declares one extension member in each extension block. In most cases, you create multiple extension members for the same receiver. In those cases, you should declare the extensions for those members in a single extension block.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
namespace NewExtensionMember;
2+
3+
// <ExtensionMemberClass>
4+
public static class EnumerableExtension
5+
{
6+
// <MedianExtensionMember>
7+
extension(IEnumerable<double>? source)
8+
{
9+
public double Median()
10+
{
11+
if (source is null || !source.Any())
12+
{
13+
throw new InvalidOperationException("Cannot compute median for a null or empty set.");
14+
}
15+
16+
var sortedList =
17+
source.OrderBy(number => number).ToList();
18+
19+
int itemIndex = sortedList.Count / 2;
20+
21+
if (sortedList.Count % 2 == 0)
22+
{
23+
// Even number of items.
24+
return (sortedList[itemIndex] + sortedList[itemIndex - 1]) / 2;
25+
}
26+
else
27+
{
28+
// Odd number of items.
29+
return sortedList[itemIndex];
30+
}
31+
}
32+
}
33+
// </MedianExtensionMember>
34+
35+
extension(IEnumerable<int> source)
36+
{
37+
public double Median() =>
38+
(from number in source select (double)number).Median();
39+
}
40+
41+
extension<T>(IEnumerable<T> source)
42+
{
43+
public double Median(Func<T, double> selector) =>
44+
(from num in source select selector(num)).Median();
45+
46+
public IEnumerable<T> AlternateElements()
47+
{
48+
int index = 0;
49+
foreach (T element in source)
50+
{
51+
if (index % 2 == 0)
52+
{
53+
yield return element;
54+
}
55+
56+
index++;
57+
}
58+
}
59+
}
60+
}
61+
// </ExtensionMemberClass>

0 commit comments

Comments
 (0)