diff --git a/docs/csharp/advanced-topics/interface-implementation/mixins-with-default-interface-methods.md b/docs/csharp/advanced-topics/interface-implementation/mixins-with-default-interface-methods.md index e420e3dc527e4..3a93c9ac0c83a 100644 --- a/docs/csharp/advanced-topics/interface-implementation/mixins-with-default-interface-methods.md +++ b/docs/csharp/advanced-topics/interface-implementation/mixins-with-default-interface-methods.md @@ -1,7 +1,7 @@ --- title: Create mixin types using default interface methods description: Using default interface members you can extend interfaces with optional default implementations for implementors. -ms.date: 07/31/2024 +ms.date: 11/24/2025 --- # Tutorial: Mix functionality in when creating classes using interfaces with default interface methods @@ -19,13 +19,13 @@ In this tutorial, you learn how to: You need to set up your machine to run .NET, including the C# compiler. The C# compiler is available with [Visual Studio 2022](https://visualstudio.microsoft.com/downloads), or the [.NET SDK](https://dotnet.microsoft.com/download/dotnet). -## Limitations of extension methods +## Limitations of extensions -One way you can implement behavior that appears as part of an interface is to define [extension methods](../../programming-guide/classes-and-structs/extension-methods.md) that provide the default behavior. Interfaces declare a minimum set of members while providing a greater surface area for any class that implements that interface. For example, the extension methods in provide the implementation for any sequence to be the source of a LINQ query. +One way you can implement behavior that appears as part of an interface is to define [extension members](../../programming-guide/classes-and-structs/extension-methods.md) that provide the default behavior. Interfaces declare a minimum set of members while providing a greater surface area for any class that implements that interface. For example, the extension members in provide the implementation for any sequence to be the source of a LINQ query. -Extension methods are resolved at compile time, using the declared type of the variable. Classes that implement the interface can provide a better implementation for any extension method. Variable declarations must match the implementing type to enable the compiler to choose that implementation. When the compile-time type matches the interface, method calls resolve to the extension method. Another concern with extension methods is that those methods are accessible wherever the class containing the extension methods is accessible. Classes can't declare if they should or shouldn't provide features declared in extension methods. +Extension members are resolved at compile time, using the declared type of the variable. Classes that implement the interface can provide a better implementation for any extension member. Variable declarations must match the implementing type to enable the compiler to choose that implementation. When the compile-time type matches the interface, method calls resolve to the extension member. Another concern with extension members is that those members are accessible wherever the class containing the extension member is accessible. Classes can't declare if they should or shouldn't provide features declared in extension members. -You can declare the default implementations as interface methods. Then, every class automatically uses the default implementation. Any class that can provide a better implementation can override the interface method definition with a better algorithm. In one sense, this technique sounds similar to how you could use [extension methods](../../programming-guide/classes-and-structs/extension-methods.md). +You can declare the default implementations as interface methods. Then, every class automatically uses the default implementation. Any class that can provide a better implementation can override the interface method definition with a better algorithm. In one sense, this technique sounds similar to how you could use [extension members](../../programming-guide/classes-and-structs/extension-methods.md). In this article, you learn how default interface implementations enable new scenarios. @@ -38,7 +38,7 @@ Consider a home automation application. You probably have many different types o Some of these extended capabilities could be emulated in devices that support the minimal set. That indicates providing a default implementation. For those devices that have more capabilities built in, the device software would use the native capabilities. For other lights, they could choose to implement the interface and use the default implementation. -Default interface members provide a better solution for this scenario than extension methods. Class authors can control which interfaces they choose to implement. Those interfaces they choose are available as methods. In addition, because default interface methods are virtual by default, the method dispatch always chooses the implementation in the class. +Default interface members provide a better solution for this scenario than extension members. Class authors can control which interfaces they choose to implement. Those interfaces they choose are available as methods. In addition, because default interface methods are virtual by default, the method dispatch always chooses the implementation in the class. Let's create the code to demonstrate these differences. diff --git a/docs/csharp/advanced-topics/interface-implementation/snippets/mixins-with-default-interface-methods/mixins-with-interfaces.csproj b/docs/csharp/advanced-topics/interface-implementation/snippets/mixins-with-default-interface-methods/mixins-with-interfaces.csproj index 08cdb396f1014..6f6cafcaa7818 100644 --- a/docs/csharp/advanced-topics/interface-implementation/snippets/mixins-with-default-interface-methods/mixins-with-interfaces.csproj +++ b/docs/csharp/advanced-topics/interface-implementation/snippets/mixins-with-default-interface-methods/mixins-with-interfaces.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net10.0 enable enable mixins_with_interfaces diff --git a/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/deconstruct-extension1.cs b/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/deconstruct-extension1.cs index d5b9db924ede5..2ae1b453546fa 100644 --- a/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/deconstruct-extension1.cs +++ b/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/deconstruct-extension1.cs @@ -4,84 +4,87 @@ public static class ReflectionExtensions { - public static void Deconstruct(this PropertyInfo p, out bool isStatic, - out bool isReadOnly, out bool isIndexed, - out Type propertyType) + extension(PropertyInfo propertyInfo) { - var getter = p.GetMethod; + public void Deconstruct(out bool isStatic, + out bool isReadOnly, out bool isIndexed, + out Type propertyType) + { + var getter = propertyInfo.GetMethod; - // Is the property read-only? - isReadOnly = ! p.CanWrite; + // Is the property read-only? + isReadOnly = ! propertyInfo.CanWrite; - // Is the property instance or static? - isStatic = getter.IsStatic; + // Is the property instance or static? + isStatic = getter.IsStatic; - // Is the property indexed? - isIndexed = p.GetIndexParameters().Length > 0; + // Is the property indexed? + isIndexed = propertyInfo.GetIndexParameters().Length > 0; - // Get the property type. - propertyType = p.PropertyType; - } + // Get the property type. + propertyType = propertyInfo.PropertyType; + } - public static void Deconstruct(this PropertyInfo p, out bool hasGetAndSet, - out bool sameAccess, out string access, - out string getAccess, out string setAccess) - { - hasGetAndSet = sameAccess = false; - string getAccessTemp = null; - string setAccessTemp = null; + public void Deconstruct(out bool hasGetAndSet, + out bool sameAccess, out string access, + out string getAccess, out string setAccess) + { + hasGetAndSet = sameAccess = false; + string getAccessTemp = null; + string setAccessTemp = null; - MethodInfo getter = null; - if (p.CanRead) - getter = p.GetMethod; + MethodInfo getter = null; + if (propertyInfo.CanRead) + getter = propertyInfo.GetMethod; - MethodInfo setter = null; - if (p.CanWrite) - setter = p.SetMethod; + MethodInfo setter = null; + if (propertyInfo.CanWrite) + setter = propertyInfo.SetMethod; - if (setter != null && getter != null) - hasGetAndSet = true; + if (setter != null && getter != null) + hasGetAndSet = true; - if (getter != null) - { - if (getter.IsPublic) - getAccessTemp = "public"; - else if (getter.IsPrivate) - getAccessTemp = "private"; - else if (getter.IsAssembly) - getAccessTemp = "internal"; - else if (getter.IsFamily) - getAccessTemp = "protected"; - else if (getter.IsFamilyOrAssembly) - getAccessTemp = "protected internal"; - } + if (getter != null) + { + if (getter.IsPublic) + getAccessTemp = "public"; + else if (getter.IsPrivate) + getAccessTemp = "private"; + else if (getter.IsAssembly) + getAccessTemp = "internal"; + else if (getter.IsFamily) + getAccessTemp = "protected"; + else if (getter.IsFamilyOrAssembly) + getAccessTemp = "protected internal"; + } - if (setter != null) - { - if (setter.IsPublic) - setAccessTemp = "public"; - else if (setter.IsPrivate) - setAccessTemp = "private"; - else if (setter.IsAssembly) - setAccessTemp = "internal"; - else if (setter.IsFamily) - setAccessTemp = "protected"; - else if (setter.IsFamilyOrAssembly) - setAccessTemp = "protected internal"; - } + if (setter != null) + { + if (setter.IsPublic) + setAccessTemp = "public"; + else if (setter.IsPrivate) + setAccessTemp = "private"; + else if (setter.IsAssembly) + setAccessTemp = "internal"; + else if (setter.IsFamily) + setAccessTemp = "protected"; + else if (setter.IsFamilyOrAssembly) + setAccessTemp = "protected internal"; + } - // Are the accessibility of the getter and setter the same? - if (setAccessTemp == getAccessTemp) - { - sameAccess = true; - access = getAccessTemp; - getAccess = setAccess = String.Empty; - } - else - { - access = null; - getAccess = getAccessTemp; - setAccess = setAccessTemp; + // Are the accessibility of the getter and setter the same? + if (setAccessTemp == getAccessTemp) + { + sameAccess = true; + access = getAccessTemp; + getAccess = setAccess = String.Empty; + } + else + { + access = null; + getAccess = getAccessTemp; + setAccess = setAccessTemp; + } } } } diff --git a/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/deconstruction.csproj b/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/deconstruction.csproj index 2de6b53d97405..d816b6e17d787 100644 --- a/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/deconstruction.csproj +++ b/docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/deconstruction.csproj @@ -2,7 +2,7 @@ Exe - net9.0 + net10.0 enable deconstruction.Program diff --git a/docs/csharp/how-to/index.md b/docs/csharp/how-to/index.md index 67cb38bbe6abc..67bca16daef6d 100644 --- a/docs/csharp/how-to/index.md +++ b/docs/csharp/how-to/index.md @@ -15,8 +15,8 @@ There are several tips and tricks that are common C# developer practices: - [Initialize objects using an object initializer](../programming-guide/classes-and-structs/how-to-initialize-objects-by-using-an-object-initializer.md). - [Use operator overloading](../language-reference/operators/operator-overloading.md). -- [Implement and call a custom extension method](../programming-guide/classes-and-structs/how-to-implement-and-call-a-custom-extension-method.md). -- [Create a new method for an `enum` type using extension methods](../programming-guide/classes-and-structs/how-to-create-a-new-method-for-an-enumeration.md). +- [Implement and call a custom extension member](../programming-guide/classes-and-structs/how-to-implement-and-call-a-custom-extension-method.md). +- [Create a new method for an `enum` type using extension member](../programming-guide/classes-and-structs/how-to-create-a-new-method-for-an-enumeration.md). ### Class, record, and struct members diff --git a/docs/csharp/language-reference/attributes/caller-information.md b/docs/csharp/language-reference/attributes/caller-information.md index 0196d1a3200a4..3d5f13a3ac509 100644 --- a/docs/csharp/language-reference/attributes/caller-information.md +++ b/docs/csharp/language-reference/attributes/caller-information.md @@ -58,7 +58,7 @@ The compiler injects the expression used for `condition` into the `message` argu Argument failed validation: ``` -This attribute enables you to write diagnostic utilities that provide more details. Developers can more quickly understand what changes are needed. You can also use the to determine what expression was used as the receiver for extension methods. The following method samples a sequence at regular intervals. If the sequence has fewer elements than the frequency, it reports an error: +This attribute enables you to write diagnostic utilities that provide more details. Developers can more quickly understand what changes are needed. You can also use the to determine what expression was used as the receiver for extension members. The following method samples a sequence at regular intervals. If the sequence has fewer elements than the frequency, it reports an error: :::code language="csharp" source="./snippets/CallerInformation.cs" id="ExtensionMethod"::: diff --git a/docs/csharp/language-reference/attributes/snippets/CallerInformation.cs b/docs/csharp/language-reference/attributes/snippets/CallerInformation.cs index 3c6fdc405542a..5cf69742b1339 100644 --- a/docs/csharp/language-reference/attributes/snippets/CallerInformation.cs +++ b/docs/csharp/language-reference/attributes/snippets/CallerInformation.cs @@ -56,16 +56,19 @@ public static void ValidateArgument(string parameterName, bool condition, [Calle public static class Extensions { // - public static IEnumerable Sample(this IEnumerable sequence, int frequency, - [CallerArgumentExpression(nameof(sequence))] string? message = null) + extension(IEnumerable sequence) { - if (sequence.Count() < frequency) - throw new ArgumentException($"Expression doesn't have enough elements: {message}", nameof(sequence)); - int i = 0; - foreach (T item in sequence) + public IEnumerable Sample(int frequency, + [CallerArgumentExpression(nameof(sequence))] string? message = null) { - if (i++ % frequency == 0) - yield return item; + if (sequence.Count() < frequency) + throw new InvalidOperationException($"Expression doesn't have enough elements: {message}"); + int i = 0; + foreach (T item in sequence) + { + if (i++ % frequency == 0) + yield return item; + } } } // diff --git a/docs/csharp/language-reference/attributes/snippets/attributes.csproj b/docs/csharp/language-reference/attributes/snippets/attributes.csproj index 0e8ab5607efbd..0acafad4a4eb5 100644 --- a/docs/csharp/language-reference/attributes/snippets/attributes.csproj +++ b/docs/csharp/language-reference/attributes/snippets/attributes.csproj @@ -2,7 +2,7 @@ Exe - net9.0 + net10.0 enable true enable diff --git a/docs/csharp/language-reference/builtin-types/built-in-types.md b/docs/csharp/language-reference/builtin-types/built-in-types.md index a50c0306a5645..1552091b27f1f 100644 --- a/docs/csharp/language-reference/builtin-types/built-in-types.md +++ b/docs/csharp/language-reference/builtin-types/built-in-types.md @@ -1,7 +1,7 @@ --- title: "Built-in types" description: "Learn C# built-in value and reference types" -ms.date: 03/07/2025 +ms.date: 11/24/2025 helpviewer_keywords: - "types [C#], built-in" - "built-in C# types" @@ -62,7 +62,7 @@ The C# language includes specialized rules for the ` to `System.ReadOnlySpan`, when `E` has a covariance conversion or an identity conversion to `U` - From `string` to `System.ReadOnlySpan` -The compiler never ignores any user defined conversion where an applicable *implicit span conversion* exists. Implicit span conversions can be applied to the first argument of [extension methods](../../programming-guide/classes-and-structs/extension-methods.md), the parameter with the `this` modifier. Implicit span conversions aren't considered for method group conversions. +The compiler never ignores any user defined conversion where an applicable *implicit span conversion* exists. Implicit span conversions can be applied to receiver parameter of [extension members](../../programming-guide/classes-and-structs/extension-methods.md). The receiver parameter is specified by the [`extension`](../keywords/extension.md) keyword in an extension member. The receiver parameter is the first parameter of an extension method using the `this` modifier. Implicit span conversions aren't considered for method group conversions. ## See also diff --git a/docs/csharp/language-reference/builtin-types/enum.md b/docs/csharp/language-reference/builtin-types/enum.md index 0f592c5245dbf..c68037ab98c30 100644 --- a/docs/csharp/language-reference/builtin-types/enum.md +++ b/docs/csharp/language-reference/builtin-types/enum.md @@ -1,7 +1,7 @@ --- title: "Enumeration types" description: "Learn about C# enumeration types that represent a choice or a combination of choices" -ms.date: 12/13/2019 +ms.date: 11/24/2025 f1_keywords: - "enum" - "enum_CSharpKeyword" @@ -10,7 +10,6 @@ helpviewer_keywords: - "enum type [C#]" - "enumeration type [C#]" - "bit flags [C#]" -ms.assetid: bbeb9a0f-e9b3-41ab-b0a6-c41b1a08974c --- # Enumeration types (C# reference) @@ -38,7 +37,7 @@ enum ErrorCode : ushort } ``` -You cannot define a method inside the definition of an enumeration type. To add functionality to an enumeration type, create an [extension method](../../programming-guide/classes-and-structs/extension-methods.md). +You can't define a method inside the definition of an enumeration type. To add functionality to an enumeration type, create an [extension member](../../programming-guide/classes-and-structs/extension-methods.md). The default value of an enumeration type `E` is the value produced by expression `(E)0`, even if zero doesn't have the corresponding enum member. @@ -50,7 +49,7 @@ C# allows implicit conversions from the literal value `0` to any enum type, and In the preceding example, both `port1` and `port2` are assigned the value `0`, but `GpioPort` has no member with that value. The method confirms these are invalid enum values. -This implicit conversion exists because the 0 bit pattern is the default for all struct types, including all enum types. However, it can introduce bugs in your code. To avoid these issues: +This implicit conversion exists because the 0-bit pattern is the default for all struct types, including all enum types. However, it can introduce bugs in your code. To avoid these issues: - You should almost always define a member with value `0` in your enums. - Use to validate enum values when converting from numeric types. @@ -62,7 +61,7 @@ You use an enumeration type to represent a choice from a set of mutually exclusi If you want an enumeration type to represent a combination of choices, define enum members for those choices such that an individual choice is a bit field. That is, the associated values of those enum members should be the powers of two. Then, you can use the [bitwise logical operators `|` or `&`](../operators/bitwise-and-shift-operators.md#enumeration-logical-operators) to combine choices or intersect combinations of choices, respectively. To indicate that an enumeration type declares bit fields, apply the [Flags](xref:System.FlagsAttribute) attribute to it. As the following example shows, you can also include some typical combinations in the definition of an enumeration type. -[!code-csharp[enum flags](snippets/shared/EnumType.cs#Flags)] +:::code language="csharp" source="snippets/shared/EnumType.cs" id="SnippetFlags"::: For more information and examples, see the API reference page and the [Non-exclusive members and the Flags attribute](../../../fundamentals/runtime-libraries/system-enum.md#non-exclusive-members-and-the-flags-attribute) section of the API reference page. @@ -76,7 +75,7 @@ You can use `System.Enum` in a base class constraint (that is known as the [enum For any enumeration type, there exist explicit conversions between the enumeration type and its underlying integral type. If you [cast](../operators/type-testing-and-cast.md#cast-expression) an enum value to its underlying type, the result is the associated integral value of an enum member. -[!code-csharp[enum conversions](snippets/shared/EnumType.cs#Conversions)] +:::code language="csharp" source="snippets/shared/EnumType.cs" id="SnippetConversions"::: Use the method to determine whether an enumeration type contains an enum member with the certain associated value. diff --git a/docs/csharp/language-reference/compiler-messages/cs0122.md b/docs/csharp/language-reference/compiler-messages/cs0122.md index 7de0976893ec7..860076f52f402 100644 --- a/docs/csharp/language-reference/compiler-messages/cs0122.md +++ b/docs/csharp/language-reference/compiler-messages/cs0122.md @@ -14,7 +14,7 @@ ms.assetid: 5f639a66-c807-4166-b88a-93e5f272ceb7 The [access modifier](../keywords/index.md) for a class member prevents accessing the member. For more information, see [Access Modifiers](../../programming-guide/classes-and-structs/access-modifiers.md). - [Extension methods](../../programming-guide/classes-and-structs/extension-methods.md) cannot access private members of the type they are extending. + [Extension members](../../programming-guide/classes-and-structs/extension-methods.md) cannot access private members of the type they are extending. One cause of this (not shown in the sample below) can be omitting the **/out** compiler flag on the target of a friend assembly. For more information, see [Friend Assemblies](../../../standard/assembly/friend.md) and [**OutputAssembly** (C# Compiler Options)](../compiler-options/output.md#outputassembly). diff --git a/docs/csharp/language-reference/keywords/method-parameters.md b/docs/csharp/language-reference/keywords/method-parameters.md index 187d2c450bb57..638daa84a3282 100644 --- a/docs/csharp/language-reference/keywords/method-parameters.md +++ b/docs/csharp/language-reference/keywords/method-parameters.md @@ -80,7 +80,7 @@ You can't use the previous parameter modifiers in the following kinds of methods - Async methods, which you define by using the [async](async.md) modifier. - Iterator methods, which include a [yield return](../statements/yield.md) or `yield break` statement. -[Extension methods](../../programming-guide/classes-and-structs/extension-methods.md) also have restrictions on the use of these argument keywords: +[Extension members](../../programming-guide/classes-and-structs/extension-methods.md) also have restrictions on the use of these argument keywords: - The `out` keyword can't be used on the first argument of an extension method. - The `ref` keyword can't be used on the first argument of an extension method when the argument isn't a `struct`, or a generic type not constrained to be a struct. diff --git a/docs/csharp/language-reference/keywords/snippets/GenericWhereConstraints.cs b/docs/csharp/language-reference/keywords/snippets/GenericWhereConstraints.cs index 154d3d6cba2c0..73ad9d5eee8c8 100644 --- a/docs/csharp/language-reference/keywords/snippets/GenericWhereConstraints.cs +++ b/docs/csharp/language-reference/keywords/snippets/GenericWhereConstraints.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using System.Text; -using static Keywords.UnmanagedExtensions; - namespace Keywords { // @@ -84,213 +82,11 @@ public void MyMethod(T t) where T : IMyInterface { } // } - // - public class Employee - { - public Employee(string s, int i) => (Name, ID) = (s, i); - public string Name { get; set; } - public int ID { get; set; } - } - - public class GenericList where T : Employee - { - private class Node - { - public Node(T t) => (Next, Data) = (null, t); - - public Node Next { get; set; } - public T Data { get; set; } - } - - private Node head; - - public void AddHead(T t) - { - Node n = new Node(t) { Next = head }; - head = n; - } - - public IEnumerator GetEnumerator() - { - Node current = head; - - while (current != null) - { - yield return current.Data; - current = current.Next; - } - } - - public T FindFirstOccurrence(string s) - { - Node current = head; - T t = null; - - while (current != null) - { - //The constraint enables access to the Name property. - if (current.Data.Name == s) - { - t = current.Data; - break; - } - else - { - current = current.Next; - } - } - return t; - } - } - // public interface IEmployee { } - // - class EmployeeList where T : Employee, IEmployee, System.IComparable, new() - { - // ... - } - // - - // - class Base { } - class Test - where U : struct - where T : Base, new() - { } - // - - namespace ListExample - { - // - public class List - { - public void Add(List items) where U : T {/*...*/} - } - // - } - - // - //Type parameter V is used as a type constraint. - public class SampleClass where T : V { } - // - - public static class UnmanagedExtensions - { - // - unsafe public static byte[] ToByteArray(this T argument) where T : unmanaged - { - var size = sizeof(T); - var result = new Byte[size]; - Byte* p = (byte*)&argument; - for (var i = 0; i < size; i++) - result[i] = *p++; - return result; - } - // - - // - public static TDelegate TypeSafeCombine(this TDelegate source, TDelegate target) - where TDelegate : System.Delegate - => Delegate.Combine(source, target) as TDelegate; - // - - // - public static Dictionary EnumNamedValues() where T : System.Enum - { - var result = new Dictionary(); - var values = Enum.GetValues(typeof(T)); - - foreach (int item in values) - result.Add(item, Enum.GetName(typeof(T), item)); - return result; - } - // - } - public static class GenericWhereConstraints - { - public static void Examples() - { - TestStringEquality(); - TestUnmanaged(); - TestDelegateCombination(); - TestEnumValues(); - } - - // - public static void OpEqualsTest(T s, T t) where T : class - { - System.Console.WriteLine(s == t); - } - private static void TestStringEquality() - { - string s1 = "target"; - System.Text.StringBuilder sb = new System.Text.StringBuilder("target"); - string s2 = sb.ToString(); - OpEqualsTest(s1, s2); - } - // - - public struct Point3D - { - public double X { get; set; } - public double Y { get; set; } - public double Z { get; set; } - } - private static void TestUnmanaged() - { - var thing = new Point3D { X = 1, Y = 2, Z = 3 }; - - var storage = thing.ToByteArray(); - - for (int i = 0; i < storage.Length; i++) - Console.Write($"{storage[i]:X2}, "); - Console.WriteLine(); - } - - private static void TestDelegateCombination() - { - // - Action first = () => Console.WriteLine("this"); - Action second = () => Console.WriteLine("that"); - - var combined = first.TypeSafeCombine(second); - combined(); - - Func test = () => true; - // Combine signature ensures combined delegates must - // have the same type. - //var badCombined = first.TypeSafeCombine(test); - // - } - - // - enum Rainbow - { - Red, - Orange, - Yellow, - Green, - Blue, - Indigo, - Violet - } - // - private static void TestEnumValues() - { - // - var map = EnumNamedValues(); - - foreach (var pair in map) - Console.WriteLine($"{pair.Key}:\t{pair.Value}"); - - // - } - } - // public abstract class B { diff --git a/docs/csharp/language-reference/keywords/snippets/Program.cs b/docs/csharp/language-reference/keywords/snippets/Program.cs index 338dbd7afa91e..cc724a7ef5d9c 100644 --- a/docs/csharp/language-reference/keywords/snippets/Program.cs +++ b/docs/csharp/language-reference/keywords/snippets/Program.cs @@ -8,8 +8,6 @@ class Program { static async Task Main(string[] args) { - Console.WriteLine("================= Generic Where Constraints Examples ======================"); - GenericWhereConstraints.Examples(); Console.WriteLine("================= readonly Keyword Examples ======================"); ReadonlyKeywordExamples.Examples(); Console.WriteLine("================= pass by value / reference Keyword Examples ======================"); diff --git a/docs/csharp/language-reference/keywords/this.md b/docs/csharp/language-reference/keywords/this.md index 87cad355b6d9e..41ebf325ffc61 100644 --- a/docs/csharp/language-reference/keywords/this.md +++ b/docs/csharp/language-reference/keywords/this.md @@ -13,7 +13,7 @@ helpviewer_keywords: The `this` keyword refers to the current instance of the class and is also used as a modifier of the first parameter of an extension method. > [!NOTE] -> This article discusses the use of `this` with class instances. For more information about its use in extension methods, see the [`extension`](./extension.md) keyword. +> This article discusses the use of `this` to refer to the receiver instance in the current member. For more information about its use in extension methods, see the [`extension`](./extension.md) keyword. The following are common uses of `this`: diff --git a/docs/csharp/language-reference/keywords/using-directive.md b/docs/csharp/language-reference/keywords/using-directive.md index 60c46dfd5f1fc..5838320f2e559 100644 --- a/docs/csharp/language-reference/keywords/using-directive.md +++ b/docs/csharp/language-reference/keywords/using-directive.md @@ -1,7 +1,7 @@ --- description: "The `using` directive imports types from a namespace, or creates an alias for a given type. Using directives enable you to use simple names for types instead of the fully qualified type name." title: "The using directive: Import types from a namespace" -ms.date: 01/27/2025 +ms.date: 11/24/2025 f1_keywords: - "using_CSharpKeyword" - "using-static_CSharpKeyword" @@ -88,7 +88,7 @@ using static ; The `` is the name of the type whose static members and nested types can be referenced without specifying a type name. If you don't provide a fully qualified type name (the full namespace name along with the type name), C# generates compiler error [CS0246](../compiler-messages/assembly-references.md): "The type or namespace name 'type/namespace' couldn't be found (are you missing a using directive or an assembly reference?)". -If the `using static` directive is applied within the context of a namespace (either file-scoped or nested in a `namespace` block, it is not necessary to fully qualify the type. +If the `using static` directive is applied within the context of a namespace (either file-scoped or nested in a `namespace` block, it isn't necessary to fully qualify the type. The `using static` directive applies to any type that has static members (or nested types), even if it also has instance members. However, instance members can only be invoked through the type instance. @@ -116,7 +116,7 @@ By eliminating the need to explicitly reference the class eac `using static` imports only accessible static members and nested types declared in the specified type. Inherited members aren't imported. You can import from any named type with a `using static` directive, including Visual Basic modules. If F# top-level functions appear in metadata as static members of a named type whose name is a valid C# identifier, then the F# functions can be imported. -`using static` makes extension methods declared in the specified type available for extension method lookup. However, the names of the extension methods aren't imported into scope for unqualified reference in code. +`using static` makes extension members declared in the specified type available for extension member lookup. However, the names of the extension members aren't imported into scope for unqualified reference in code. Methods with the same name imported from different types by different `using static` directives in the same compilation unit or namespace form a method group. Overload resolution within these method groups follows normal C# rules. diff --git a/docs/csharp/language-reference/keywords/where-generic-type-constraint.md b/docs/csharp/language-reference/keywords/where-generic-type-constraint.md index 6e81ba5502889..41d6398125641 100644 --- a/docs/csharp/language-reference/keywords/where-generic-type-constraint.md +++ b/docs/csharp/language-reference/keywords/where-generic-type-constraint.md @@ -2,7 +2,7 @@ description: "where (generic type constraint) - C# Reference" title: "where (generic type constraint)" -ms.date: 07/26/2024 +ms.date: 11/24/2024 f1_keywords: - "whereconstraint" - "whereconstraint_CSharpKeyword" @@ -47,7 +47,7 @@ You use the `default` constraint to specify that your derived class overrides th :::code language="csharp" source="snippets/GenericWhereConstraints.cs" ID="DerivedClass"::: > [!IMPORTANT] -> Generic declarations that include the `notnull` constraint can be used in a nullable oblivious context, but compiler does not enforce the constraint. +> Generic declarations that include the `notnull` constraint can be used in a nullable oblivious context, but compiler doesn't enforce the constraint. :::code language="csharp" source="snippets/GenericWhereConstraints.cs" ID="NotNull"::: diff --git a/docs/csharp/language-reference/operators/deconstruction.md b/docs/csharp/language-reference/operators/deconstruction.md index 989c27a6bce9b..6f0f812396e06 100644 --- a/docs/csharp/language-reference/operators/deconstruction.md +++ b/docs/csharp/language-reference/operators/deconstruction.md @@ -1,7 +1,7 @@ --- title: "Deconstruction expression - extract properties or fields from a tuple or other type" description: "Learn about deconstruction expressions: expressions that extract individual properties or fields from a tuple or user defined type into discrete expressions." -ms.date: 12/17/2024 +ms.date: 11/24/2025 --- # Deconstruction expression - Extract properties of fields from a tuple or other user-defined type @@ -37,7 +37,7 @@ You can make use of this behavior to specify which properties of your record typ ## Declare `Deconstruct` methods -You can add deconstruction support to any class, struct, or interface you declare. You declare one or more `Deconstruct` methods in your type, or as extension methods on that type. A deconstruction expression calls a method `void Deconstruct(out var p1, ..., out var pn)`. The `Deconstruct` method can be either an instance method or an extension method. The type of each parameter in the `Deconstruct` method must match the type of the corresponding argument in the deconstruction expression. The deconstruction expression assigns the value of each argument to the value of the corresponding `out` parameter in the `Deconstruct` method. If multiple `Deconstruct` methods match the deconstruction expression, the compiler reports an error for the ambiguity. +You can add deconstruction support to any class, struct, or interface you declare. You declare one or more `Deconstruct` methods in your type, or as extension on that type. A deconstruction expression calls a method `void Deconstruct(out var p1, ..., out var pn)`. The `Deconstruct` method can be either an instance method or an extension method. The type of each parameter in the `Deconstruct` method must match the type of the corresponding argument in the deconstruction expression. The deconstruction expression assigns the value of each argument to the value of the corresponding `out` parameter in the `Deconstruct` method. If multiple `Deconstruct` methods match the deconstruction expression, the compiler reports an error for the ambiguity. The following code declares a `Point3D` struct that has two `Deconstruct` methods: diff --git a/docs/csharp/language-reference/operators/member-access-operators.md b/docs/csharp/language-reference/operators/member-access-operators.md index 9dda13cf2fd61..5ae030ea32f5c 100644 --- a/docs/csharp/language-reference/operators/member-access-operators.md +++ b/docs/csharp/language-reference/operators/member-access-operators.md @@ -1,7 +1,7 @@ --- title: "Member access and null-conditional operators and expressions:" description: "C# operators that you use to access type members or null-conditionally access type members. These operators include the dot operator - `.`, indexers - `[`, `]`, `^` and `..`, and invocation - `(`, `)`." -ms.date: 05/29/2025 +ms.date: 11/24/2025 author: pkulikov f1_keywords: - "._CSharpKeyword" @@ -62,7 +62,7 @@ You use the `.` token to access a member of a namespace or a type, as the follow :::code language="csharp" source="snippets/shared/MemberAccessOperators.cs" id="TypeMemberAccess" interactive="try-dotnet-method"::: -You can also use `.` to access an [extension method](../../programming-guide/classes-and-structs/extension-methods.md). +You can also use `.` to access an [extension member](../../programming-guide/classes-and-structs/extension-methods.md). ## Indexer operator [] diff --git a/docs/csharp/language-reference/statements/iteration-statements.md b/docs/csharp/language-reference/statements/iteration-statements.md index ebb4f882894b0..0da59b3abb0d9 100644 --- a/docs/csharp/language-reference/statements/iteration-statements.md +++ b/docs/csharp/language-reference/statements/iteration-statements.md @@ -83,7 +83,7 @@ The `foreach` statement executes a statement or a block of statements for each e The `foreach` statement isn't limited to those types. You can use it with an instance of any type that satisfies the following conditions: -- A type has the public parameterless `GetEnumerator` method. The `GetEnumerator` method can be a type's [extension method](../../programming-guide/classes-and-structs/extension-methods.md). +- A type has the public parameterless `GetEnumerator` method. The `GetEnumerator` method can be an [extension method](../../programming-guide/classes-and-structs/extension-methods.md). - The return type of the `GetEnumerator` method has the public `Current` property and the public parameterless `MoveNext` method whose return type is `bool`. The following example uses the `foreach` statement with an instance of the type, which doesn't implement any interfaces: @@ -98,13 +98,13 @@ If the source collection of the `foreach` statement is empty, the body of the `f ### await foreach -You can use the `await foreach` statement to consume an asynchronous stream of data, that is, the collection type that implements the interface. Each iteration of the loop may be suspended while the next element is retrieved asynchronously. The following example shows how to use the `await foreach` statement: +You can use the `await foreach` statement to consume an asynchronous stream of data, that is, the collection type that implements the interface. Each iteration of the loop can be suspended while the next element is retrieved asynchronously. The following example shows how to use the `await foreach` statement: :::code language="csharp" source="snippets/iteration-statements/ForeachStatement.cs" id="AwaitForeach" ::: You can also use the `await foreach` statement with an instance of any type that satisfies the following conditions: -- A type has the public parameterless `GetAsyncEnumerator` method. That method can be a type's [extension method](../../programming-guide/classes-and-structs/extension-methods.md). +- A type has the public parameterless `GetAsyncEnumerator` method. That method can be an [extension member](../../programming-guide/classes-and-structs/extension-methods.md). - The return type of the `GetAsyncEnumerator` method has the public `Current` property and the public parameterless `MoveNextAsync` method whose return type is [`Task`](xref:System.Threading.Tasks.Task%601), [`ValueTask`](xref:System.Threading.Tasks.ValueTask%601), or any other awaitable type whose awaiter's `GetResult` method returns a `bool` value. By default, stream elements are processed in the captured context. If you want to disable capturing of the context, use the extension method. For more information about synchronization contexts and capturing the current context, see [Consuming the Task-based asynchronous pattern](../../../standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern.md). For more information about asynchronous streams, see the [Asynchronous streams tutorial](../../asynchronous-programming/generate-consume-asynchronous-stream.md). @@ -118,8 +118,8 @@ foreach (var item in collection) { } ``` > [!NOTE] -> Type of `var` can be inferred by the compiler as a nullable reference type, depending on whether the [nullable aware context](../../language-reference/builtin-types/nullable-reference-types.md) is enabled and whether the type of an initialization expression is a reference type. -> For more information see [Implicitly-typed local variables](./declarations.md#implicitly-typed-local-variables). +> The type of a `var` declaration can be inferred by the compiler as a nullable reference type, depending on whether the [nullable aware context](../../language-reference/builtin-types/nullable-reference-types.md) is enabled and whether the type of an initialization expression is a reference type. +> For more information, see [Implicitly-typed local variables](./declarations.md#implicitly-typed-local-variables). You can also explicitly specify the type of an iteration variable, as the following code shows: @@ -128,7 +128,7 @@ IEnumerable collection = new T[5]; foreach (V item in collection) { } ``` -In the preceding form, type `T` of a collection element must be implicitly or explicitly convertible to type `V` of an iteration variable. If an explicit conversion from `T` to `V` fails at run time, the `foreach` statement throws an . For example, if `T` is a non-sealed class type, `V` can be any interface type, even the one that `T` doesn't implement. At run time, the type of a collection element may be the one that derives from `T` and actually implements `V`. If that's not the case, an is thrown. +In the preceding form, type `T` of a collection element must be implicitly or explicitly convertible to type `V` of an iteration variable. If an explicit conversion from `T` to `V` fails at run time, the `foreach` statement throws an . For example, if `T` is a non-sealed class type, `V` can be any interface type, even the one that `T` doesn't implement. At run time, the type of a collection element can be the one that derives from `T` and actually implements `V`. If that's not the case, an is thrown. ## The `do` statement diff --git a/docs/csharp/language-reference/statements/yield.md b/docs/csharp/language-reference/statements/yield.md index 83e4d4524c2b1..f9f3894df5515 100644 --- a/docs/csharp/language-reference/statements/yield.md +++ b/docs/csharp/language-reference/statements/yield.md @@ -1,7 +1,7 @@ --- title: "yield statement - provide the next element in an iterator" description: "Use the yield statement in iterators to provide the next value or signal the end of an iteration" -ms.date: 06/28/2024 +ms.date: 11/24/2025 f1_keywords: - "yield" - "yield_CSharpKeyword" @@ -38,7 +38,7 @@ You can't use the `yield` statements in: - methods with [in](../keywords/method-parameters.md#in-parameter-modifier), [ref](../keywords/ref.md), or [out](../keywords/method-parameters.md#out-parameter-modifier) parameters. - [lambda expressions](../operators/lambda-expressions.md) and [anonymous methods](../operators/delegate-operator.md). - [unsafe blocks](../keywords/unsafe.md). Before C# 13, `yield` was invalid in any method with an `unsafe` block. Beginning with C# 13, you can use `yield` in methods with `unsafe` blocks, but not in the `unsafe` block. -- `yield return` and `yield break` can not be used in [catch](../statements/exception-handling-statements.md) and [finally](../statements/exception-handling-statements.md) blocks, or in [try](../statements/exception-handling-statements.md) blocks with a corresponding `catch` block. The `yield return` and `yield break` statements can be used in a `try` block with no `catch` blocks, only a `finally` block. +- `yield return` and `yield break` can't be used in [catch](../statements/exception-handling-statements.md) and [finally](../statements/exception-handling-statements.md) blocks, or in [try](../statements/exception-handling-statements.md) blocks with a corresponding `catch` block. The `yield return` and `yield break` statements can be used in a `try` block with no `catch` blocks, only a `finally` block. ## `using` statements in iterators diff --git a/docs/csharp/methods.md b/docs/csharp/methods.md index c080032bf08c6..e77e7b50be77b 100644 --- a/docs/csharp/methods.md +++ b/docs/csharp/methods.md @@ -211,7 +211,7 @@ If a method takes an array as a parameter and modifies the value of individual e :::code language="csharp" source="snippets/methods/returnarray1.cs" id="snippet101"::: -## Extension methods +## Extension members Ordinarily, there are two ways to add a method to an existing type: diff --git a/docs/csharp/roslyn-sdk/get-started/syntax-transformation.md b/docs/csharp/roslyn-sdk/get-started/syntax-transformation.md index 5eca2838d8f9f..f6fdf6761a23f 100644 --- a/docs/csharp/roslyn-sdk/get-started/syntax-transformation.md +++ b/docs/csharp/roslyn-sdk/get-started/syntax-transformation.md @@ -57,7 +57,7 @@ Run the program again to see that you've built the tree for the code to add. ### Create a modified tree -You've built a small syntax tree that contains one statement. The APIs to create new nodes are the right choice to create single statements or other small code blocks. However, to build larger blocks of code, you should use methods that replace nodes or insert nodes into an existing tree. Remember that syntax trees are immutable. The **Syntax API** doesn't provide any mechanism for modifying an existing syntax tree after construction. Instead, it provides methods that produce new trees based on changes to existing ones. `With*` methods are defined in concrete classes that derive from or in extension methods declared in the class. These methods create a new node by applying changes to an existing node's child properties. Additionally, the extension method can be used to replace a descendent node in a subtree. This method also updates the parent to point to the newly created child and repeats this process up the entire tree - a process known as _re-spinning_ the tree. +You've built a small syntax tree that contains one statement. The APIs to create new nodes are the right choice to create single statements or other small code blocks. However, to build larger blocks of code, you should use methods that replace nodes or insert nodes into an existing tree. Remember that syntax trees are immutable. The **Syntax API** doesn't provide any mechanism for modifying an existing syntax tree after construction. Instead, it provides methods that produce new trees based on changes to existing ones. `With*` methods are defined in concrete classes that derive from or in extension members declared in the class. These methods create a new node by applying changes to an existing node's child properties. Additionally, the extension member can be used to replace a descendent node in a subtree. This method also updates the parent to point to the newly created child and repeats this process up the entire tree - a process known as _re-spinning_ the tree. The next step is to create a tree that represents an entire (small) program and then modify it. Add the following code to the beginning of the `Program` class: diff --git a/docs/csharp/roslyn-sdk/tutorials/how-to-write-csharp-analyzer-code-fix.md b/docs/csharp/roslyn-sdk/tutorials/how-to-write-csharp-analyzer-code-fix.md index 73f8d4ac449c8..f273ff10e457b 100644 --- a/docs/csharp/roslyn-sdk/tutorials/how-to-write-csharp-analyzer-code-fix.md +++ b/docs/csharp/roslyn-sdk/tutorials/how-to-write-csharp-analyzer-code-fix.md @@ -148,7 +148,7 @@ This cast always succeeds because your analyzer registered for changes to local Finally, you need to check that the variable could be `const`. That means making sure it is never assigned after it is initialized. -You'll perform some semantic analysis using the . You use the `context` argument to determine whether the local variable declaration can be made `const`. A represents all of semantic information in a single source file. You can learn more in the article that covers [semantic models](../work-with-semantics.md). You'll use the to perform data flow analysis on the local declaration statement. Then, you use the results of this data flow analysis to ensure that the local variable isn't written with a new value anywhere else. Call the extension method to retrieve the for the variable and check that it isn't contained with the collection of the data flow analysis. Add the following code to the end of the `AnalyzeNode` method: +You'll perform some semantic analysis using the . You use the `context` argument to determine whether the local variable declaration can be made `const`. A represents all of semantic information in a single source file. You can learn more in the article that covers [semantic models](../work-with-semantics.md). You'll use the to perform data flow analysis on the local declaration statement. Then, you use the results of this data flow analysis to ensure that the local variable isn't written with a new value anywhere else. Call the extension member to retrieve the for the variable and check that it isn't contained with the collection of the data flow analysis. Add the following code to the end of the `AnalyzeNode` method: ```csharp // Perform data flow analysis on the local declaration.