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
1 change: 1 addition & 0 deletions docs/core/compatibility/10.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ If you're migrating an app to .NET 10, the breaking changes listed here might af
| [Default workload configuration from 'loose manifests' to 'workload sets' mode](sdk/10.0/default-workload-config.md) | Behavioral change | Preview 2 |
| [`dotnet package list` performs restore](sdk/10.0/dotnet-package-list-restore.md) | Behavioral change | Preview 4 |
| [`dotnet restore` audits transitive packages](sdk/10.0/nugetaudit-transitive-packages.md) | Behavioral change | Preview 3 |
| [project.json not supported in `dotnet restore`](sdk/10.0/dotnet-restore-project-json-unsupported.md) | Source incompatible | Preview 7 |
| [SHA-1 fingerprint support deprecated in `dotnet nuget sign`](sdk/10.0/dotnet-nuget-sign-sha1-deprecated.md) | Behavioral change | Preview 1 |
| [MSBUILDCUSTOMBUILDEVENTWARNING escape hatch removed](sdk/10.0/custom-build-event-warning.md) | Behavioral change | Preview 1 |
| [MSBuild custom culture resource handling](sdk/10.0/msbuild-custom-culture.md) | Behavioral change | Preview 1 |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
title: "Breaking change - project.json is no longer supported in dotnet restore"
description: "Learn about the breaking change in .NET 10 where dotnet restore no longer supports project.json based projects."
ms.date: 08/16/2025
ai-usage: ai-assisted
ms.custom: https://github.com/dotnet/docs/issues/47968
---

# project.json no longer supported in dotnet restore

Starting with .NET 10, the [`dotnet restore` command](../../../tools/dotnet-restore.md) no longer supports `project.json` based projects. Such projects are ignored during the restore operation.

## Version introduced

.NET 10 Preview 7

## Previous behavior

The `dotnet restore` command restored dependencies for `project.json` based projects.

## New behavior

The `dotnet restore` command ignores `project.json` based projects and no longer restores their dependencies.

## Type of breaking change

This change can affect [source compatibility](../../categories.md#source-compatibility).

## Reason for change

The `project.json` format was originally available only in .NET Core previews (through Preview 2 of .NET Core 1.0) and was completely replaced by PackageReference in 2017. The format has been marked as deprecated since 2017.

When the `project.json` format was replaced, users migrated these projects using the [`dotnet migrate`](../../../tools/dotnet-migrate.md) command, but that command was removed from the CLI in the .NET Core 3.0 SDK.

The removal of `project.json` support completes this transition and allows the .NET team to focus on delivering a better experience for PackageReference-based projects.

## Recommended action

Migrate your `project.json` projects to use PackageReference format instead.

If you have .NET Core based `project.json` projects, you can use older versions of the .NET SDK that still include the [`dotnet migrate` command](../../../tools/dotnet-migrate.md) to convert them to the modern project format.

For more information about migrating from `project.json`, see [Migrating from project.json to .csproj](/nuget/archive/project-json#migrate-projectjson-to-packagereference).

## Affected APIs

None.
2 changes: 2 additions & 0 deletions docs/core/compatibility/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ items:
href: sdk/10.0/dotnet-tool-pack-publish.md
- name: "`dotnet restore` audits transitive packages"
href: sdk/10.0/nugetaudit-transitive-packages.md
- name: project.json not supported in `dotnet restore`
href: sdk/10.0/dotnet-restore-project-json-unsupported.md
- name: Default workload configuration from 'loose manifests' to 'workload sets' mode
href: sdk/10.0/default-workload-config.md
- name: "`dotnet package list` performs restore"
Expand Down
6 changes: 4 additions & 2 deletions docs/csharp/fundamentals/types/records.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "Record types"
description: Learn about C# record types and how to create them. A record is a class that provides value semantics.
ms.date: 05/24/2023
ms.date: 08/15/2025
helpviewer_keywords:
- "records [C#]"
- "C# language, records"
Expand Down Expand Up @@ -31,7 +31,7 @@ Immutability isn't appropriate for all data scenarios. [Entity Framework Core](/

## How records differ from classes and structs

The same syntax that [declares](classes.md#declaring-classes) and [instantiates](classes.md#creating-objects) classes or structs can be used with records. Just substitute the `class` keyword with the `record`, or use `record struct` instead of `struct`. Likewise, the same syntax for expressing inheritance relationships is supported by record classes. Records differ from classes in the following ways:
The same syntax that [declares](classes.md#declaring-classes) and [instantiates](classes.md#creating-objects) classes or structs can be used with records. Just substitute the `class` keyword with the `record`, or use `record struct` instead of `struct`. Likewise, record classes support the same syntax for expressing inheritance relationships. Records differ from classes in the following ways:

* You can use [positional parameters](../../language-reference/builtin-types/record.md#positional-syntax-for-property-and-field-definition) in a [primary constructor](../../programming-guide/classes-and-structs/instance-constructors.md#primary-constructors) to create and instantiate a type with immutable properties.
* The same methods and operators that indicate reference equality or inequality in classes (such as <xref:System.Object.Equals(System.Object)?displayProperty=nameWithType> and `==`), indicate [value equality or inequality](../../language-reference/builtin-types/record.md#value-equality) in records.
Expand All @@ -57,6 +57,8 @@ The following example demonstrates use of a `with` expression to copy an immutab

:::code language="csharp" source="./snippets/records/ImmutableRecord.cs" id="ImmutableRecord":::

In the preceding examples, all properties are independent. None of the properties are computed from other property values. A `with` expression first copies the existing record instance, then modifies any properties or fields specified in the `with` expression. Computed properties in `record` types should be computed on access, not initialized when the instance is created. Otherwise, a property could return the computed value based on the original instance, not the modified copy. If you must initialize a computed property rather than compute on access, you should consider a [`class`](./classes.md) instead of a record.

For more information, see [Records (C# reference)](../../language-reference/builtin-types/record.md).

## C# Language Specification
Expand Down
23 changes: 22 additions & 1 deletion docs/csharp/language-reference/builtin-types/record.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "Records"
description: Learn about the record modifier for class and struct types in C#. Records provide standard support for value based equality on instances of record types.
ms.date: 02/05/2025
ms.date: 08/15/2025
f1_keywords:
- "record_CSharpKeyword"
helpviewer_keywords:
Expand Down Expand Up @@ -146,6 +146,27 @@ If you need different copying behavior, you can write your own copy constructor

You can't override the clone method, and you can't create a member named `Clone` in any record type. The actual name of the clone method is compiler-generated.

> [!IMPORTANT]
> In the preceding examples, all properties are independent. None of the properties are computed from other property values. A `with` expression first copies the existing record instance, then modifies any properties or fields specified in the `with` expression. Computed properties in `record` types should be computed on access, not initialized when the instance is created. Otherwise, a property could return the computed value based on the original instance, not the modified copy.

You ensure correctness on computed properties by computing the value on access, as shown in the following declaration:

:::code language="csharp" source="snippets/shared/RecordType.cs" id="WitherComputed":::

The preceding record type computes the `Distance` when accessed, as shown in the following example:

:::code language="csharp" source="snippets/shared/RecordType.cs" id="WitherComputedUsage":::

Contrast that with the following declaration, where the `Distance` property is computed and cached as part of the initialization of a new instance:

:::code language="csharp" source="snippets/shared/RecordType.cs" id="WitherInit":::

Because `Distance` is computed as part of initialization, the value is computed and cached before the `with` expression changes the value of `Y` in the copy. The result is that the distance is incorrect:

:::code language="csharp" source="snippets/shared/RecordType.cs" id="WitherInitUsage":::

The `Distance` computation isn't expensive to compute on each access. However, some computed properties might require access to more data or more extensive computation. In those cases, instead of a record, use a `class` type and compute the cached value when one of the components changes value.

## Built-in formatting for display

Record types have a compiler-generated <xref:System.Object.ToString%2A> method that displays the names and values of public properties and fields. The `ToString` method returns a string of the following format:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ public static void Examples()
var p = new Point();
(double x, double y, double z) = p;

Console.WriteLine("=================================================");
ComputedWither.ExampleUsage.Example();

}
// <PositionalRecord>
public record Person(string FirstName, string LastName);
Expand Down Expand Up @@ -102,7 +105,7 @@ public static class PositionalAttributes
/// map to the JSON elements "firstName" and "lastName" when
/// serialized or deserialized.
/// </remarks>
public record Person([property: JsonPropertyName("firstName")] string FirstName,
public record Person([property: JsonPropertyName("firstName")] string FirstName,
[property: JsonPropertyName("lastName")] string LastName);
// </PositionalAttributes>

Expand Down Expand Up @@ -335,7 +338,8 @@ protected override bool PrintMembers(StringBuilder stringBuilder)
if (base.PrintMembers(stringBuilder))
{
stringBuilder.Append(", ");
};
}
;
stringBuilder.Append($"Grade = {Grade}");
return true;
}
Expand Down Expand Up @@ -426,4 +430,47 @@ public static void Main()
// </WithExpressionInheritance>
}
}

namespace ComputedWither
{
// <WitherComputed>
public record Point(int X, int Y)
{
public double Distance => Math.Sqrt(X * X + Y * Y);
}
// </WitherComputed>

// <WitherInit>
public record PointInit(int X, int Y)
{
public double Distance { get; } = Math.Sqrt(X * X + Y * Y);
}
// </WitherInit>

public static class ExampleUsage
{
public static void Example()
{
// <WitherComputedUsage>
Point p1 = new Point(3, 4);
Console.WriteLine($"Original point: {p1}");
p1 = p1 with { Y = 8 };
Console.WriteLine($"Modified point: {p1}");
// Output:
// Original point: Point { X = 3, Y = 4, Distance = 5 }
// Modified point: Point { X = 3, Y = 8, Distance = 8.54400374531753 }
// </WitherComputedUsage>

// <WitherInitUsage>
PointInit pt1 = new PointInit(3, 4);
Console.WriteLine($"Original point: {pt1}");
pt1 = pt1 with { Y = 8 };
Console.WriteLine($"Incorrect Modified point: {pt1}");
// Output:
// Original point: PointInit { X = 3, Y = 4, Distance = 5 }
// Modified point: PointInit { X = 3, Y = 8, Distance = 5 }
// </WitherInitUsage>
}
}
}
}
3 changes: 3 additions & 0 deletions docs/csharp/language-reference/keywords/using-directive.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,14 @@ Analyzers issue diagnostics if you duplicate `global` using directives in differ
The `using static` directive names a type whose static members and nested types you can access without specifying a type name. Its syntax is:

```csharp
// not within a namespace
using static <fully-qualified-type-name>;
```

The `<fully-qualified-type-name>` 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.

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.

You can access static members of a type without having to qualify the access with the type name:
Expand Down
2 changes: 2 additions & 0 deletions docs/csharp/language-reference/keywords/when.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ catch (ExceptionType [e]) when (expr)

where *expr* is an expression that evaluates to a Boolean value. If it returns `true`, the exception handler executes; if `false`, it does not.

Exception filters with the `when` keyword provide several advantages over traditional exception handling approaches, including better debugging support and performance benefits. For a detailed explanation of how exception filters preserve the call stack and improve debugging, see [Exception filters vs. traditional exception handling](../statements/exception-handling-statements.md#exception-filters-vs-traditional-exception-handling).

The following example uses the `when` keyword to conditionally execute handlers for an <xref:System.Net.Http.HttpRequestException> depending on the text of the exception message.

[!code-csharp[when-with-catch](~/samples/snippets/csharp/language-reference/keywords/when/catch.cs)]
Expand Down
11 changes: 7 additions & 4 deletions docs/csharp/language-reference/operators/with-expression.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
---
title: "with expression - create new objects that are modified copies of existing objects"
title: "The with expression - create new objects that are modified copies of existing objects"
description: "Learn about a with expression that performs nondestructive mutation of C# records and structures. The `with` keyword provides the means to modify one or more properties in the new object."
ms.date: 11/22/2024
ms.date: 08/15/2025
f1_keywords:
- "with_CSharpKeyword"
helpviewer_keywords:
- "with expression [C#]"
- "with operator [C#]"
---
# with expression - Nondestructive mutation creates a new object with modified properties
# The `with` expression - Nondestructive mutation creates a new object with modified properties

A `with` expression produces a copy of its operand with the specified properties and fields modified. You use the [object initializer](../../programming-guide/classes-and-structs/object-and-collection-initializers.md) syntax to specify what members to modify and their new values:

Expand All @@ -20,7 +20,7 @@ The result of a `with` expression has the same run-time type as the expression's

:::code language="csharp" source="snippets/with-expression/InheritanceExample.cs" :::

In the case of a reference-type member, only the reference to a member instance is copied when an operand is copied. Both the copy and original operand have access to the same reference-type instance. The following example demonstrates that behavior:
When a member is a reference type, only the reference to a member instance is copied when an operand is copied. Both the copy and original operand have access to the same reference-type instance. The following example demonstrates that behavior:

:::code language="csharp" source="snippets/with-expression/ExampleWithReferenceType.cs" :::

Expand All @@ -32,6 +32,9 @@ Any record class type has the *copy constructor*. A *copy constructor* is a cons

You can't customize the copy semantics for structure types.

> [!IMPORTANT]
> In the preceding examples, all properties are independent. None of the properties are computed from other property values. A `with` expression first copies the existing record instance, then modifies any properties or fields specified in the `with` expression. Computed properties in `record` types should be computed on access, not initialized when the instance is created. Otherwise, a property could return the computed value based on the original instance, not the modified copy. For more information, see the language reference article on [`record` types](../builtin-types/record.md#nondestructive-mutation).

## C# language specification

For more information, see the following sections of the [records feature proposal note](~/_csharplang/proposals/csharp-9.0/records.md):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,43 @@ You can provide several `catch` clauses for the same exception type if they dist

If a `catch` clause has an exception filter, it can specify the exception type that is the same as or less derived than an exception type of a `catch` clause that appears after it. For example, if an exception filter is present, a `catch (Exception e)` clause doesn't need to be the last clause.

##### Exception filters vs. traditional exception handling

Exception filters provide significant advantages over traditional exception handling approaches. The key difference is **when** the exception handling logic is evaluated:

- **Exception filters (`when`)**: The filter expression is evaluated *before* the stack is unwound. This means the original call stack and all local variables remain intact during filter evaluation.
- **Traditional `catch` blocks**: The catch block executes *after* the stack is unwound, potentially losing valuable debugging information.

Here's a comparison showing the difference:

:::code language="csharp" source="snippets/exception-handling-statements/WhenFilterExamples.cs" id="ExceptionFilterVsIfElse":::

## Advantages of exception filters

- **Better debugging experience**: Since the stack isn't unwound until a filter matches, debuggers can show the original point of failure with all local variables intact.
- **Performance benefits**: If no filter matches, the exception continues propagating without the overhead of stack unwinding and restoration.
- **Cleaner code**: Multiple filters can handle different conditions of the same exception type without requiring nested if-else statements.
- **Logging and diagnostics**: You can examine and log exception details before deciding whether to handle the exception:

:::code language="csharp" source="snippets/exception-handling-statements/WhenFilterExamples.cs" id="DebuggingAdvantageExample":::

### When to use exception filters

Use exception filters when you need to:

- Handle exceptions based on specific conditions or properties.
- Preserve the original call stack for debugging.
- Log or examine exceptions before deciding whether to handle them.
- Handle the same exception type differently based on context.

:::code language="csharp" source="snippets/exception-handling-statements/WhenFilterExamples.cs" id="MultipleConditionsExample":::

### Stack trace preservation

Exception filters preserve the original `ex.StackTrace` property. If a `catch` clause can't process the exception and re-throws, the original stack information is lost. The `when` filter doesn't unwind the stack, so if a `when` filter is `false`, the original stack trace isn't changed.

The exception filter approach is valuable in applications where preserving debugging information is crucial for diagnosing issues.

#### Exceptions in async and iterator methods

If an exception occurs in an [async function](../keywords/async.md), it propagates to the caller of the function when you [await](../operators/await.md) the result of the function, as the following example shows:
Expand Down
Loading
Loading