From f2c5afccb380f6e39f54966bb171a0bc50541114 Mon Sep 17 00:00:00 2001 From: Bartosz Klonowski Date: Sat, 19 Oct 2024 22:34:36 +0200 Subject: [PATCH 1/6] Implement entities showing static virtual in interface --- .../snippets/staticinterfaces/Duplicate.cs | 14 ++++++++++++++ .../snippets/staticinterfaces/GetDuplicated.cs | 4 ++++ 2 files changed, 18 insertions(+) create mode 100644 docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Duplicate.cs create mode 100644 docs/csharp/whats-new/tutorials/snippets/staticinterfaces/GetDuplicated.cs diff --git a/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Duplicate.cs b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Duplicate.cs new file mode 100644 index 0000000000000..e2f58db09c929 --- /dev/null +++ b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Duplicate.cs @@ -0,0 +1,14 @@ +public struct Duplicate : IGetDuplicated +{ + public int _num; + + public Duplicate(int num) + { + _num = num; + } + + public static Duplicate operator ++(Duplicate other) + => other with { _num = other._num + other._num }; + + public override string ToString() => _num.ToString(); +} diff --git a/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/GetDuplicated.cs b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/GetDuplicated.cs new file mode 100644 index 0000000000000..b0228691c04df --- /dev/null +++ b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/GetDuplicated.cs @@ -0,0 +1,4 @@ +public interface IGetDuplicated where T : IGetDuplicated +{ + static virtual T operator ++(T other) => throw new NotImplementedException(); +} From 0fd07904fd1f9e72bb6e886567bf066bded9ae8a Mon Sep 17 00:00:00 2001 From: Bartosz Klonowski Date: Sat, 19 Oct 2024 22:36:16 +0200 Subject: [PATCH 2/6] Utilize the static virtual methods defining in TestDuplication example --- .../tutorials/snippets/staticinterfaces/Program.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Program.cs b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Program.cs index 1be6412c1176b..f355e061d9676 100644 --- a/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Program.cs +++ b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Program.cs @@ -9,6 +9,12 @@ Console.WriteLine(str++); // +// +var num = new Duplicate(2); +num++; +Console.WriteLine(num.ToString()); +// + // var pt = new Point(3, 4); From 6929ea65efd38d6a3c5fe735f3ec9c6493762e49 Mon Sep 17 00:00:00 2001 From: Bartosz Klonowski Date: Sat, 19 Oct 2024 22:37:42 +0200 Subject: [PATCH 3/6] Describe in details the static virtual methods of an interface --- .../static-virtual-interface-members.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/csharp/whats-new/tutorials/static-virtual-interface-members.md b/docs/csharp/whats-new/tutorials/static-virtual-interface-members.md index f1811e3b1260b..b5a2f2f790d97 100644 --- a/docs/csharp/whats-new/tutorials/static-virtual-interface-members.md +++ b/docs/csharp/whats-new/tutorials/static-virtual-interface-members.md @@ -66,6 +66,26 @@ AAAAAAAAAA This small example demonstrates the motivation for this feature. You can use natural syntax for operators, constant values, and other static operations. You can explore these techniques when you create multiple types that rely on static members, including overloaded operators. Define the interfaces that match your types' capabilities and then declare those types' support for the new interface. +## Static virtual interface methods + +Similar can be achieved using the static virtual methods of an interface. This is done with the syntax of `static` and `virtual` modifiers added to any member that should be implemented in the type implementing that interface. The following example defines the `IGetDuplicated` interface, providing any type with a constraint that a type will implement the `operator ++`: + +:::code language="csharp" source="./snippets/staticinterfaces/GetDuplicated.cs"::: + +Because virtual methods aren't abstract, they must declare their body. However, for now it's throwing the `NotImplementedException` exception by default, to highlight the lack of implementation. + +The next snippet creates a structure that implements the `IGetDuplicated` interface. This `Duplicate` structure uses the `++` operator implementation to duplicate the initial number given when creating the structure: + +:::code language="csharp" source="./snippets/staticinterfaces/Duplicate.cs"::: + +You can use this `Duplicate` type by calling the `operator ++`: + +:::code language="csharp" source="./snippets/staticinterfaces/Program.cs" id="TestDuplication"::: + +which will print the result of `4`. + +Note that without having the `operator ++` implemented, calling `num++` would still not compile, leading to [CS0023](../../misc/cs0023.md). Reason for that is that even if we have the default implementation in the static virtual method of an interface, this is still only an interface and the type implementing that interface has no such operator implemented. + ## Generic math The motivating scenario for allowing static methods, including operators, in interfaces is to support [generic math](../../../standard/generics/math.md) algorithms. The .NET 7 base class library contains interface definitions for many arithmetic operators, and derived interfaces that combine many arithmetic operators in an `INumber` interface. Let's apply those types to build a `Point` record that can use any numeric type for `T`. The point can be moved by some `XOffset` and `YOffset` using the `+` operator. From 51c71a6cf581b0edca453832068e045b3848164b Mon Sep 17 00:00:00 2001 From: Bartosz Klonowski Date: Thu, 28 Aug 2025 23:15:59 +0200 Subject: [PATCH 4/6] Reimplement the example of static virtual to present the Singleton approach --- .../snippets/staticinterfaces/Duplicate.cs | 14 -------------- .../snippets/staticinterfaces/GetDuplicated.cs | 4 ---- .../snippets/staticinterfaces/Program.cs | 9 ++++----- .../snippets/staticinterfaces/Singleton.cs | 15 +++++++++++++++ .../snippets/staticinterfaces/UniqueInstance.cs | 7 +++++++ .../staticinterfaces/staticinterfaces.csproj | 1 + 6 files changed, 27 insertions(+), 23 deletions(-) delete mode 100644 docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Duplicate.cs delete mode 100644 docs/csharp/whats-new/tutorials/snippets/staticinterfaces/GetDuplicated.cs create mode 100644 docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Singleton.cs create mode 100644 docs/csharp/whats-new/tutorials/snippets/staticinterfaces/UniqueInstance.cs diff --git a/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Duplicate.cs b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Duplicate.cs deleted file mode 100644 index e2f58db09c929..0000000000000 --- a/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Duplicate.cs +++ /dev/null @@ -1,14 +0,0 @@ -public struct Duplicate : IGetDuplicated -{ - public int _num; - - public Duplicate(int num) - { - _num = num; - } - - public static Duplicate operator ++(Duplicate other) - => other with { _num = other._num + other._num }; - - public override string ToString() => _num.ToString(); -} diff --git a/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/GetDuplicated.cs b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/GetDuplicated.cs deleted file mode 100644 index b0228691c04df..0000000000000 --- a/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/GetDuplicated.cs +++ /dev/null @@ -1,4 +0,0 @@ -public interface IGetDuplicated where T : IGetDuplicated -{ - static virtual T operator ++(T other) => throw new NotImplementedException(); -} diff --git a/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Program.cs b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Program.cs index f355e061d9676..7ae85b9025010 100644 --- a/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Program.cs +++ b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Program.cs @@ -9,11 +9,10 @@ Console.WriteLine(str++); // -// -var num = new Duplicate(2); -num++; -Console.WriteLine(num.ToString()); -// +// +var instance = UniqueInstance.Instance; +instance.PrintMessage(); +// // var pt = new Point(3, 4); diff --git a/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Singleton.cs b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Singleton.cs new file mode 100644 index 0000000000000..46e2e8e1f6712 --- /dev/null +++ b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Singleton.cs @@ -0,0 +1,15 @@ +public interface ISingleton where T : new() +{ + public static virtual T Instance + { + get + { + field ??= new T(); + return field; + } + private set + { + field = value; + } + } +} diff --git a/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/UniqueInstance.cs b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/UniqueInstance.cs new file mode 100644 index 0000000000000..66f6bf4a7d9c2 --- /dev/null +++ b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/UniqueInstance.cs @@ -0,0 +1,7 @@ +public class UniqueInstance : ISingleton +{ + public void PrintMessage() + { + Console.WriteLine("This is a unique instance"); + } +} diff --git a/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/staticinterfaces.csproj b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/staticinterfaces.csproj index 2150e3797ba5e..160a73d9a758f 100644 --- a/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/staticinterfaces.csproj +++ b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/staticinterfaces.csproj @@ -2,6 +2,7 @@ Exe + preview net8.0 enable enable From f0c613d5909c4414f74be9311c5cc541aeb72a18 Mon Sep 17 00:00:00 2001 From: Bartosz Klonowski Date: Thu, 28 Aug 2025 23:18:33 +0200 Subject: [PATCH 5/6] Rephrase the description to mention ISingleton and rephrase the entry --- .../static-virtual-interface-members.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/docs/csharp/whats-new/tutorials/static-virtual-interface-members.md b/docs/csharp/whats-new/tutorials/static-virtual-interface-members.md index b5a2f2f790d97..5e1dcb5199a31 100644 --- a/docs/csharp/whats-new/tutorials/static-virtual-interface-members.md +++ b/docs/csharp/whats-new/tutorials/static-virtual-interface-members.md @@ -68,23 +68,21 @@ This small example demonstrates the motivation for this feature. You can use nat ## Static virtual interface methods -Similar can be achieved using the static virtual methods of an interface. This is done with the syntax of `static` and `virtual` modifiers added to any member that should be implemented in the type implementing that interface. The following example defines the `IGetDuplicated` interface, providing any type with a constraint that a type will implement the `operator ++`: +You can also provide a default implementation for static methods defined in an interface. This is done with the syntax of `static` and `virtual` modifiers added to any member that should be implemented in the type implementing that interface. The following example defines the `ISingleton` interface, providing any type with a constraint that a type will implement the unique `Instance` field: -:::code language="csharp" source="./snippets/staticinterfaces/GetDuplicated.cs"::: +:::code language="csharp" source="./snippets/staticinterfaces/Singleton.cs"::: -Because virtual methods aren't abstract, they must declare their body. However, for now it's throwing the `NotImplementedException` exception by default, to highlight the lack of implementation. +Because virtual methods aren't abstract, they must declare their body. In this case the body is the implementation of `get` and `set` of the unique `Instance` that a singleton will share. -The next snippet creates a structure that implements the `IGetDuplicated` interface. This `Duplicate` structure uses the `++` operator implementation to duplicate the initial number given when creating the structure: +The next snippet creates a class that implements the `ISingleton` interface. This `UniqueInstance` class does not override the behavior of `PrintMessage()` method, but relies on it's original definition: -:::code language="csharp" source="./snippets/staticinterfaces/Duplicate.cs"::: +:::code language="csharp" source="./snippets/staticinterfaces/UniqueInstance.cs"::: -You can use this `Duplicate` type by calling the `operator ++`: +You can use this `UniqueInstance` type by calling the `PrintMessage()` method: -:::code language="csharp" source="./snippets/staticinterfaces/Program.cs" id="TestDuplication"::: +:::code language="csharp" source="./snippets/staticinterfaces/Program.cs" id="TestUniqueInstance"::: -which will print the result of `4`. - -Note that without having the `operator ++` implemented, calling `num++` would still not compile, leading to [CS0023](../../misc/cs0023.md). Reason for that is that even if we have the default implementation in the static virtual method of an interface, this is still only an interface and the type implementing that interface has no such operator implemented. +which will print the message defined in the `ISingleton` interface. ## Generic math From 69a3f65e7e5bd419397c4acfa9531da4387774f6 Mon Sep 17 00:00:00 2001 From: Bartosz Klonowski <70535775+BartoszKlonowski@users.noreply.github.com> Date: Thu, 28 Aug 2025 23:39:19 +0200 Subject: [PATCH 6/6] Cast UniqueInstance to ISingleton to access Instance Co-authored-by: Bill Wagner --- .../whats-new/tutorials/snippets/staticinterfaces/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Program.cs b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Program.cs index 7ae85b9025010..7a2a291374cc7 100644 --- a/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Program.cs +++ b/docs/csharp/whats-new/tutorials/snippets/staticinterfaces/Program.cs @@ -10,7 +10,7 @@ // // -var instance = UniqueInstance.Instance; +var instance = (UniqueInstance as ISingleton)?.Instance; instance.PrintMessage(); //