Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion docs/core/tools/rid-specific-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Create RID-specific, self-contained, and AOT .NET tools
description: Learn how to create and package RID-specific, self-contained, and AOT .NET tools for platform-specific distribution.
ms.topic: how-to
ms.date: 11/12/2025
ms.date: 01/26/2026
ai-usage: ai-assisted
---

Expand Down Expand Up @@ -61,6 +61,32 @@ Alternatively, use `ToolPackageRuntimeIdentifiers` for tool-specific RID configu

Use a semicolon-delimited list of RID values. For a list of Runtime Identifiers, see the [RID catalog](../rid-catalog.md).

### Provide a framework-dependent version

When you opt into RID-specific tool packaging, the .NET SDK creates self-contained packages for each specified RID. However, you lose the default framework-dependent packaging that works on any platform with the .NET runtime installed.

To provide a framework-dependent version alongside RID-specific packages, add `any` to your `ToolPackageRuntimeIdentifiers` list:

```xml
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<ToolPackageRuntimeIdentifiers>win-x64;osx-arm64;linux-x64;any</ToolPackageRuntimeIdentifiers>
</PropertyGroup>
```

This configuration creates five packages:

- One top-level pointer package that lists all available sub-packages.
- Three RID-specific packages (`win-x64`, `osx-arm64`, `linux-x64`) that are self-contained and trimmed.
- One RID-agnostic package (`any`) that is framework-dependent and requires the .NET runtime to be installed.

When users install your tool:

- On `win-x64`, `osx-arm64`, or `linux-x64` systems, the self-contained, trimmed packages are downloaded and executed.
- On any other operating system or architecture, the framework-dependent `any` package is used.

This approach provides optimized, self-contained binaries for common platforms while maintaining compatibility with less common platforms through the framework-dependent fallback.

### When to use `RuntimeIdentifiers` vs `ToolPackageRuntimeIdentifiers`

Both `RuntimeIdentifiers` and `ToolPackageRuntimeIdentifiers` opt your tool into RID-specific packaging, but they serve slightly different purposes:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ ms.date: 05/23/2025

The following errors are generated when source generators or interceptors are loaded during a compilation:

- [**CS9137**](#interceptors-are-experimental): *The 'interceptors' experimental feature is not enabled. Add `<Features>InterceptorsPreview<Features>` to your project.*
- [**CS9137**](#interceptors-are-experimental): *The 'interceptors' experimental feature is not enabled. Add `<Features>InterceptorsPreview</Features>` to your project.*
- [**CS9138**](#incorrect-interceptor-declaration): *Method cannot be used as an interceptor because it or its containing type has type parameters.*
- [**CS9139**](#incorrect-mapping): *Cannot intercept: compilation does not contain a file with path.*
- [**CS9140**](#incorrect-mapping): *Cannot intercept: compilation does not contain a file with path. Did you mean to use a different path?*
Expand Down Expand Up @@ -126,11 +126,11 @@ These errors and warnings follow these themes:

This error indicates you must enable the experimental feature.

- **CS9137**: *The 'interceptors' experimental feature is not enabled. Add `<Features>InterceptorsPreview<Features>` to your project.*
- **CS9137**: *The 'interceptors' experimental feature is not enabled. Add `<Features>InterceptorsPreview</Features>` to your project.*

In C# 12, interceptors are experimental. Interceptors are subject to breaking changes or removal in a future release. Therefore, it is not recommended for production or released applications.

In order to use interceptors, you must set the `<Features>InterceptorsPreview<Features>` element in your project file. Without this flag, interceptors are disabled, even when other C# 12 features are enabled.
In order to use interceptors, you must set the `<Features>InterceptorsPreview</Features>` element in your project file. Without this flag, interceptors are disabled, even when other C# 12 features are enabled.

## Signature mismatch

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,15 @@ public IEnumerable<TReceiver> Prepend<TArg>(IEnumerable<TArg> second, Func<TArg,
yield return item;
}
}

/// <summary>
/// Gets an empty sequence of the receiver's element type.
/// </summary>
/// <remarks>
/// This property can be used as a neutral starting point when aggregating or composing
/// sequences of the receiver's element type. The returned sequence is always empty and does not allocate any storage.
/// </remarks>
public static IEnumerable<TReceiver> Identity => Enumerable.Empty<TReceiver>();
}
}
// </GenericExtension>
Expand Down
2 changes: 1 addition & 1 deletion docs/csharp/whats-new/tutorials/extension-members.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ In this tutorial, you:

## Create the sample application

Start by creating a console application that demonstrates both traditional extension methods and the new extension members syntax.
Start by creating a console application that demonstrates both traditional extension methods and the new extension members syntax. You'll create extensions for the <xref:System.Drawing.Point?displayProperty=fullName> type. This type comes from the `System.Drawing` namespace and is typically used in Windows Forms applications.

1. Create a new console application:

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
// <Snippet10>
using System;

public class Example1
{
public static void Main()
{
// <Snippet10>
float value1 = 10.201438f;
value1 = (float)Math.Sqrt((float)Math.Pow(value1, 2));
float value2 = (float)Math.Pow((float)value1 * 3.51f, 2);
value2 = ((float)Math.Sqrt(value2)) / 3.51f;
Console.WriteLine($"{value1} = {value2}: {value1.Equals(value2)}");
Console.WriteLine();
Console.WriteLine($"{value1:G9} = {value2:G9}");

// The example displays the following output on .NET:
// 10.201438 = 10.201439: False
// The example displays the following output on .NET Framework:
// 10.20144 = 10.20144: False
// </Snippet10>
}
}
// The example displays the following output:
// 10.20144 = 10.20144: False
//
// 10.201438 = 10.2014389
// </Snippet10>
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
// <Snippet11>
using System;

public class Example2
{
public static void Main()
{
// <Snippet11>
float value1 = .3333333f;
float value2 = 1.0f / 3;
int precision = 7;
value1 = (float)Math.Round(value1, precision);
value2 = (float)Math.Round(value2, precision);
Console.WriteLine($"{value1:R} = {value2:R}: {value1.Equals(value2)}");

// The example displays the following output:
// 0.3333333 = 0.3333333: True
// </Snippet11>
}
}
// The example displays the following output:
// 0.3333333 = 0.3333333: True
// </Snippet11>
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// <Snippet12>
using System;

public class Example3
{
// <Snippet12>
public static void Main()
{
float one1 = .1f * 10;
Expand All @@ -13,7 +13,7 @@ public static void Main()
Console.WriteLine($"{one1:R} = {one2:R}: {one1.Equals(one2)}");
Console.WriteLine($"{one1:R} is approximately equal to {one2:R}: " +
$"{IsApproximatelyEqual(one1, one2, .000001f)}");

float negativeOne1 = -1 * one1;
float negativeOne2 = -1 * one2;

Expand All @@ -34,16 +34,18 @@ static bool IsApproximatelyEqual(float value1, float value2, float epsilon)
else if (Double.IsInfinity(value2) | Double.IsNaN(value2))
return value1.Equals(value2);

// Handle zero to avoid division by zero
// Handle zero to avoid division by zero.
double divisor = Math.Max(value1, value2);
if (divisor.Equals(0))
divisor = Math.Min(value1, value2);

return Math.Abs((value1 - value2) / divisor) <= epsilon;
}

// The example displays the following output on .NET:
// 1 = 1.0000001: False
// 1 is approximately equal to 1.0000001: True
// -1 = -1.0000001: False
// -1 is approximately equal to -1.0000001: True
// </Snippet12>
}
// The example displays the following output:
// 1 = 1.00000012: False
// 1 is approximately equal to 1.00000012: True
// -1 is approximately equal to -1.00000012: True
// </Snippet12>
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// <Snippet21>
using System;

public class Example5
{
public static void Main()
{
// <Snippet21>
float[] values = { Single.MinValue, -67890.1234f, -12345.6789f,
12345.6789f, 67890.1234f, Single.MaxValue,
Single.NaN, Single.PositiveInfinity,
Expand Down Expand Up @@ -46,98 +46,53 @@ public static void Main()
Console.WriteLine();
}
}

// The example displays the following output for conversions performed
// in a checked context:
// Unable to convert -3.402823E+38 to Int64.
// Unable to convert -3.402823E+38 to UInt64.
// Unable to convert -3.402823E+38 to Decimal.
// -3.402823E+38 (Single) --> -3.40282346638529E+38 (Double)
//
// -67890.13 (Single) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
// Unable to convert -67890.13 to UInt64.
// -67890.13 (Single) --> -67890.12 (Decimal)
// -67890.13 (Single) --> -67890.125 (Double)
//
// -12345.68 (Single) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
// Unable to convert -12345.68 to UInt64.
// -12345.68 (Single) --> -12345.68 (Decimal)
// -12345.68 (Single) --> -12345.6787109375 (Double)
//
// 12345.68 (Single) --> 12345 (0x0000000000003039) (Int64)
// 12345.68 (Single) --> 12345 (0x0000000000003039) (UInt64)
// 12345.68 (Single) --> 12345.68 (Decimal)
// 12345.68 (Single) --> 12345.6787109375 (Double)
//
// 67890.13 (Single) --> 67890 (0x0000000000010932) (Int64)
// 67890.13 (Single) --> 67890 (0x0000000000010932) (UInt64)
// 67890.13 (Single) --> 67890.12 (Decimal)
// 67890.13 (Single) --> 67890.125 (Double)
//
// Unable to convert 3.402823E+38 to Int64.
// Unable to convert 3.402823E+38 to UInt64.
// Unable to convert 3.402823E+38 to Decimal.
// 3.402823E+38 (Single) --> 3.40282346638529E+38 (Double)
//
// Unable to convert NaN to Int64.
// Unable to convert NaN to UInt64.
// Unable to convert NaN to Decimal.
// NaN (Single) --> NaN (Double)
//
// Unable to convert ∞ to Int64.
// Unable to convert ∞ to UInt64.
// Unable to convert ∞ to Decimal.
// ∞ (Single) --> ∞ (Double)
//
// Unable to convert -∞ to Int64.
// Unable to convert -∞ to UInt64.
// Unable to convert -∞ to Decimal.
// -∞ (Single) --> -∞ (Double)
// </Snippet21>
}
}
// The example displays the following output for conversions performed
// in a checked context:
// Unable to convert -3.402823E+38 to Int64.
// Unable to convert -3.402823E+38 to UInt64.
// Unable to convert -3.402823E+38 to Decimal.
// -3.402823E+38 (Single) --> -3.40282346638529E+38 (Double)
//
// -67890.13 (Single) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
// Unable to convert -67890.13 to UInt64.
// -67890.13 (Single) --> -67890.12 (Decimal)
// -67890.13 (Single) --> -67890.125 (Double)
//
// -12345.68 (Single) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
// Unable to convert -12345.68 to UInt64.
// -12345.68 (Single) --> -12345.68 (Decimal)
// -12345.68 (Single) --> -12345.6787109375 (Double)
//
// 12345.68 (Single) --> 12345 (0x0000000000003039) (Int64)
// 12345.68 (Single) --> 12345 (0x0000000000003039) (UInt64)
// 12345.68 (Single) --> 12345.68 (Decimal)
// 12345.68 (Single) --> 12345.6787109375 (Double)
//
// 67890.13 (Single) --> 67890 (0x0000000000010932) (Int64)
// 67890.13 (Single) --> 67890 (0x0000000000010932) (UInt64)
// 67890.13 (Single) --> 67890.12 (Decimal)
// 67890.13 (Single) --> 67890.125 (Double)
//
// Unable to convert 3.402823E+38 to Int64.
// Unable to convert 3.402823E+38 to UInt64.
// Unable to convert 3.402823E+38 to Decimal.
// 3.402823E+38 (Single) --> 3.40282346638529E+38 (Double)
//
// Unable to convert NaN to Int64.
// Unable to convert NaN to UInt64.
// Unable to convert NaN to Decimal.
// NaN (Single) --> NaN (Double)
//
// Unable to convert Infinity to Int64.
// Unable to convert Infinity to UInt64.
// Unable to convert Infinity to Decimal.
// Infinity (Single) --> Infinity (Double)
//
// Unable to convert -Infinity to Int64.
// Unable to convert -Infinity to UInt64.
// Unable to convert -Infinity to Decimal.
// -Infinity (Single) --> -Infinity (Double)
// The example displays the following output for conversions performed
// in an unchecked context:
// -3.402823E+38 (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
// -3.402823E+38 (Single) --> 9223372036854775808 (0x8000000000000000) (UInt64)
// Unable to convert -3.402823E+38 to Decimal.
// -3.402823E+38 (Single) --> -3.40282346638529E+38 (Double)
//
// -67890.13 (Single) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
// -67890.13 (Single) --> 18446744073709483726 (0xFFFFFFFFFFFEF6CE) (UInt64)
// -67890.13 (Single) --> -67890.12 (Decimal)
// -67890.13 (Single) --> -67890.125 (Double)
//
// -12345.68 (Single) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
// -12345.68 (Single) --> 18446744073709539271 (0xFFFFFFFFFFFFCFC7) (UInt64)
// -12345.68 (Single) --> -12345.68 (Decimal)
// -12345.68 (Single) --> -12345.6787109375 (Double)
//
// 12345.68 (Single) --> 12345 (0x0000000000003039) (Int64)
// 12345.68 (Single) --> 12345 (0x0000000000003039) (UInt64)
// 12345.68 (Single) --> 12345.68 (Decimal)
// 12345.68 (Single) --> 12345.6787109375 (Double)
//
// 67890.13 (Single) --> 67890 (0x0000000000010932) (Int64)
// 67890.13 (Single) --> 67890 (0x0000000000010932) (UInt64)
// 67890.13 (Single) --> 67890.12 (Decimal)
// 67890.13 (Single) --> 67890.125 (Double)
//
// 3.402823E+38 (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
// 3.402823E+38 (Single) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert 3.402823E+38 to Decimal.
// 3.402823E+38 (Single) --> 3.40282346638529E+38 (Double)
//
// NaN (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
// NaN (Single) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert NaN to Decimal.
// NaN (Single) --> NaN (Double)
//
// Infinity (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
// Infinity (Single) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert Infinity to Decimal.
// Infinity (Single) --> Infinity (Double)
//
// -Infinity (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
// -Infinity (Single) --> 9223372036854775808 (0x8000000000000000) (UInt64)
// Unable to convert -Infinity to Decimal.
// -Infinity (Single) --> -Infinity (Double)
// </Snippet21>
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
// <Snippet1>
using System;

public class Example6
{
public static void Main()
{
// <Snippet1>
float value1 = 1.163287e-36f;
float value2 = 9.164234e-25f;
float result = value1 * value2;
Console.WriteLine($"{value1} * {value2} = {result}");
Console.WriteLine($"{result} = 0: {result.Equals(0.0f)}");

// The example displays the following output:
// 1.163287E-36 * 9.164234E-25 = 0
// 0 = 0: True
// </Snippet1>
}
}
// The example displays the following output:
// 1.163287E-36 * 9.164234E-25 = 0
// 0 = 0: True
// </Snippet1>
Loading
Loading