Create deno.yml#1
Conversation
Mister-g666
commented
Feb 10, 2026
<PropertyGroup>
<!--- ... -->
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
<!--- ... -->
</PropertyGroup>
|
{% raw %}${{ env.MY_VARIABLE }}{% endraw %} |
Extension members[!INCLUDESpecletdisclaimer] Champion issue: dotnet/csharplang#8697 DeclarationSyntaxclass_body
: '{' class_member_declaration* '}' ';'?
| ';'
;
class_member_declaration
: constant_declaration
| field_declaration
| method_declaration
| property_declaration
| event_declaration
| indexer_declaration
| operator_declaration
| constructor_declaration
| finalizer_declaration
| static_constructor_declaration
| type_declaration
| extension_declaration // add
;
extension_declaration // add
: 'extension' type_parameter_list? '(' receiver_parameter ')' type_parameter_constraints_clause* extension_body
;
extension_body // add
: '{' extension_member_declaration* '}' ';'?
;
extension_member_declaration // add
: method_declaration
| property_declaration
| operator_declaration
;
receiver_parameter // add
: attributes? parameter_modifiers? type identifier?
;Extension declarations shall only be declared in non-generic, non-nested static classes. Scoping rulesThe type parameters and receiver parameter of an extension declaration are in scope within the body of the extension declaration. It is an error to refer to the receiver parameter from within a static member, except within a public static class E
{
extension<T>(T[] ts)
{
public bool M1(T t) => ts.Contains(t); // `T` and `ts` are in scope
public static bool M2(T t) => ts.Contains(t); // Error: Cannot refer to `ts` from static context
public void M3(int T, string ts) { } // Error: Cannot reuse names `T` and `ts`
public void M4<T, ts>(string s) { } // Error: Cannot reuse names `T` and `ts`
}
}It is not an error for the members themselves to have the same name as the type parameters or receiver parameter of the enclosing extension declaration. Member names are not directly found in a simple name lookup from within the extension declaration; lookup will thus find the type parameter or receiver parameter of that name, rather than the member. Members do give rise to static methods being declared directly on the enclosing static class, and those can be found via simple name lookup; however, an extension declaration type parameter or receiver parameter of the same name will be found first. public static class E
{
extension<T>(T[] ts)
{
public void T() { M(ts); } // Generated static method M<T>(T[]) is found
public void M() { T(ts); } // Error: T is a type parameter
}
}Static classes as extension containersExtensions are declared inside top-level non-generic static classes, just like extension methods today, public static class Enumerable
{
// New extension declaration
extension(IEnumerable source) { ... }
// Classic extension method
public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source) { ... }
// Non-extension member
public static IEnumerable<int> Range(int start, int count) { ... }
}Extension declarationsAn extension declaration is anonymous, and provides a receiver specification with any associated type parameters and constraints, public static class Enumerable
{
extension(IEnumerable source) // extension members for IEnumerable
{
public bool IsEmpty { get { ... } }
}
extension<TSource>(IEnumerable<TSource> source) // extension members for IEnumerable<TSource>
{
public IEnumerable<T> Where(Func<TSource, bool> predicate) { ... }
public IEnumerable<TResult> Select<TResult>(Func<TSource, TResult> selector) { ... }
}
extension<TElement>(IEnumerable<TElement>) // static extension members for IEnumerable<TElement>
where TElement : INumber<TElement>
{
public static IEnumerable<TElement> operator +(IEnumerable<TElement> first, IEnumerable<TElement> second) { ... }
}
}The type in the receiver specification is referred to as the receiver type and the parameter name, If the receiver parameter is named, the receiver type may not be static. Extension membersExtension member declarations are syntactically identical to corresponding instance and static members public static class Enumerable
{
extension(IEnumerable source)
{
// 'source' refers to receiver
public bool IsEmpty => !source.GetEnumerator().MoveNext();
}
}It is an error to specify an instance extension member public static class Enumerable
{
extension(IEnumerable) // No parameter name
{
public bool IsEmpty => true; // Error: instance extension member not allowed
}
}It is an error to specify the following modifiers on a member of an extension declaration: All members shall have names that differ from the name of the static enclosing class and the name of the extended type if it has one. It is an error to decorate an extension member with the RefnessBy default the receiver is passed to instance extension members by value, just like other parameters. Nullability and attributesReceiver types can be or contain nullable reference types, and receiver specifications that are in the form of parameters can specify attributes: public static class NullableExtensions
{
extension(string? text)
{
public string AsNotNull => text is null ? "" : text;
}
extension([NotNullWhen(false)] string? text)
{
public bool IsNullOrEmpty => text is null or [];
}
extension<T> ([NotNull] T t) where T : class?
{
public void ThrowIfNull() => ArgumentNullException.ThrowIfNull(t);
}
}Compatibility with classic extension methodsInstance extension methods generate artifacts that match those produced by classic extension methods. Specifically the generated static method has the attributes, modifiers and name of the declared extension method, public static class Enumerable
{
extension<TSource>(IEnumerable<TSource> source) // Generate compatible extension methods
{
public IEnumerable<TSource> Where(Func<TSource, bool> predicate) { ... }
public IEnumerable<TSource> Select<TResult>(Func<TSource, TResult> selector) { ... }
}
}Generates: [Extension]
public static class Enumerable
{
[Extension]
public static IEnumerable<TSource> Where<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate) { ... }
[Extension]
public static IEnumerable<TSource> Select<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> selector) { ... }
}OperatorsAlthough extension operators have explicit operand types, they still need to be declared within an extension declaration: public static class Enumerable
{
extension<TElement>(IEnumerable<TElement>) where TElement : INumber<TElement>
{
public static IEnumerable<TElement> operator *(IEnumerable<TElement> vector, TElement scalar) { ... }
public static IEnumerable<TElement> operator *(TElement scalar, IEnumerable<TElement> vector) { ... }
}
}This allows type parameters to be declared and inferred, and is analogous to how a regular user-defined operator must be declared within one of its operand types. CheckingInferrability: For each non-method extension member, all the type parameters of its extension block must be used in the combined set of parameters Uniqueness: Within a given enclosing static class, the set of extension member declarations with the same receiver type public static class MyExtensions
{
extension<T1>(IEnumerable<int>) // Error! T1 not inferrable
{
...
}
extension<T2>(IEnumerable<T2>)
{
public bool IsEmpty { get ... }
}
extension<T3>(IEnumerable<T3>?)
{
public bool IsEmpty { get ... } // Error! Duplicate declaration
}
}The application of this uniqueness rule includes classic extension methods within the same static class. public static class Enumerable
{
public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source) { ... }
extension(IEnumerable source)
{
IEnumerable<TResult> Cast<TResult>() { ... } // Error! Duplicate declaration
}
}ConsumptionWhen an extension member lookup is attempted, all extension declarations within static classes that are string[] strings = ...;
var query = strings.Select(s => s.Length); // extension invocation
var query2 = strings.Select<string, int>(s => s.Length); // ... with explicit full set of type arguments
var query3 = Enumerable.Select(strings, s => s.Length); // static method invocation
var query4 = Enumerable.Where<string, int>(strings, s => s.Length); // ... with explicit full set of type arguments
public static class Enumerable
{
extension<TSource>(IEnumerable<TSource> source)
{
public IEnumerable<TResult> Select<TResult>(Func<T, TResult> predicate) { ... }
}
}Similarly to classic extension methods, the emitted implementation methods can be invoked statically. object.M(); // ambiguous
E1.M();
new object().M2(); // ambiguous
E1.M2(new object());
_ = _new object().P; // ambiguous
_ = E1.get_P(new object());
static class E1
{
extension(object)
{
public static void M() { }
public void M2() { }
public int P => 42;
}
}
static class E2
{
extension(object)
{
public static void M() { }
public void M2() { }
public int P => 42;
}
}Static extension methods will be resolved like instance extension methods (we will consider an extra argument of the receiver type).
|
|