rksoftware

Visual Studio とか C# とかが好きです

C# 12 を試す .NET 8 RC1 編

そろそろ C# 12 の時期なので予習を始めなければなりません。
今回は .NET 8 RC1 が出たので、その環境で C# 12 を書いてみます。

> dotnet --version
8.0.100-rc.1.23455.8

ちゃんと RC1 環境です。
プロジェクトを作っていきます。

> dotnet new console

出来上がったファイルです。

> ls
obj
ConsoleAppNetRC1.csproj
Program.cs

■ C# 12 のコードを書いてみる

作った C# 12 のコードを書いてみます。C# 12 のコードはこちらの記事から。
rksoftware.hatenablog.com

Program.cs

using System.Runtime.CompilerServices;

using MyType = (int x, int y);

// See https://aka.ms/new-console-template for more information
Console.WriteLine(new Test("Hello, World! 1").Name());

Console.WriteLine(new MyType(1, 2));

int[] row0 = [1, 2, 3];
int[] row1 = [4, 5, 6];
int[] row2 = [7, 8, 9];
int[] single = [.. row0, .. row1, .. row2];
foreach (var element in single)
{
    Console.Write($"{element}, ");
}

var lamda = (int a = 1) => a + a;
Console.WriteLine(lamda());

var buffer = new Buffer();
for (int i = 0; i < 10; i++)
{
    buffer[i] = i;
}

foreach (var i in buffer)
{
    Console.WriteLine(i);
}

var c = new C();
c.InterceptableMethod(1); // (L1,C1): prints "interceptor 1"
c.InterceptableMethod(1); // (L2,C2): prints "other interceptor 1"
c.InterceptableMethod(2); // (L3,C3): prints "other interceptor 2"
c.InterceptableMethod(1); // prints "interceptable 1"



class Test(string name)
{
    public string Name() { return name; }
}

[System.Runtime.CompilerServices.InlineArray(10)]
public struct Buffer
{
    private int _element0;
}

class C
{
    public void InterceptableMethod(int param)
    {
        Console.WriteLine($"interceptable {param}");
    }
}

// generated code
static class D
{
    [InterceptsLocation(@"C:\Sample\cs12\ConsoleAppVSPreview\ConsoleAppVSPreview\Program.cs", line: 36, character: 3)] // refers to the call at (L1, C1)
    public static void InterceptorMethod(this C c, int param)
    {
        Console.WriteLine($"interceptor {param}");
    }

    [InterceptsLocation(@"C:\Sample\cs12\ConsoleAppVSPreview\ConsoleAppVSPreview\Program.cs", line: 37, character: 3)] // refers to the call at (L2, C2)
    //[InterceptsLocation(@"C:\Sample\cs12\ConsoleAppVSPreview\ConsoleAppVSPreview\Program.cs", line: 38, character: 3)] // refers to the call at (L3, C3)
    public static void OtherInterceptorMethod(this C c, int param)
    {
        Console.WriteLine($"other interceptor {param}");
    }
}

namespace System.Runtime.CompilerServices
{
    sealed class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
    {
    }
}

.csproj は編集しないでやってみます。

■ .csproj は編集しない

> dotnet build
MSBuild のバージョン 17.8.0-preview-23418-03+0125fc9fb (.NET)
  復元対象のプロジェクトを決定しています...
  復元対象のすべてのプロジェクトは最新です。
Program.cs(63,6): error CS9137: 試験的な機能である 'interceptors' が有効になっていません。'<Features>InterceptorsPreview</Features>' をプロジェクトに追加します。
Program.cs(69,6): error CS9137: 試験的な機能である 'interceptors' が有効になっていません。'<Features>InterceptorsPreview</Features>' をプロジェクトに追加します。
ビルドに失敗しました。

ビルドに失敗しました。
しかし見てください、構文エラーはありません! インターセプターが実験的機能過ぎて設定が必要と言われているだけです。

ということで、インターセプターを消してみます。

using System.Runtime.CompilerServices;

using MyType = (int x, int y);

// See https://aka.ms/new-console-template for more information
Console.WriteLine(new Test("Hello, World! 1").Name());

Console.WriteLine(new MyType(1, 2));

int[] row0 = [1, 2, 3];
int[] row1 = [4, 5, 6];
int[] row2 = [7, 8, 9];
int[] single = [.. row0, .. row1, .. row2];
foreach (var element in single)
{
    Console.Write($"{element}, ");
}

var lamda = (int a = 1) => a + a;
Console.WriteLine(lamda());

var buffer = new Buffer();
for (int i = 0; i < 10; i++)
{
    buffer[i] = i;
}

foreach (var i in buffer)
{
    Console.WriteLine(i);
}

var c = new C();
c.InterceptableMethod(1); // (L1,C1): prints "interceptor 1"
c.InterceptableMethod(1); // (L2,C2): prints "other interceptor 1"
c.InterceptableMethod(2); // (L3,C3): prints "other interceptor 2"
c.InterceptableMethod(1); // prints "interceptable 1"



class Test(string name)
{
    public string Name() { return name; }
}

[System.Runtime.CompilerServices.InlineArray(10)]
public struct Buffer
{
    private int _element0;
}

class C
{
    public void InterceptableMethod(int param)
    {
        Console.WriteLine($"interceptable {param}");
    }
}

// generated code
static class D
{
//    [InterceptsLocation(@"C:\Sample\cs12\ConsoleAppVSPreview\ConsoleAppVSPreview\Program.cs", line: 36, character: 3)] // refers to the call at (L1, C1)
    public static void InterceptorMethod(this C c, int param)
    {
        Console.WriteLine($"interceptor {param}");
    }

//    [InterceptsLocation(@"C:\Sample\cs12\ConsoleAppVSPreview\ConsoleAppVSPreview\Program.cs", line: 37, character: 3)] // refers to the call at (L2, C2)
    //[InterceptsLocation(@"C:\Sample\cs12\ConsoleAppVSPreview\ConsoleAppVSPreview\Program.cs", line: 38, character: 3)] // refers to the call at (L3, C3)
    public static void OtherInterceptorMethod(this C c, int param)
    {
        Console.WriteLine($"other interceptor {param}");
    }
}

namespace System.Runtime.CompilerServices
{
    sealed class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
    {
    }
}

■ 計画通り

> dotnet build

ビルドに成功しました。
> dotnet run
Hello, World! 1
(1, 2)
1, 2, 3, 4, 5, 6, 7, 8, 9, 2
0
1
2
3
4
5
6
7
8
9
interceptable 1
interceptable 1
interceptable 2
interceptable 1

計画通り実行までできました。

■ 言語バージョン

今回、.csproj に <LangVersion>Preview</LangVersion> を書きませんでした。.NET 8 RC1 ではC# 12 はプレビュー機能ではなくなったという事ですね! 夢広がりますな~。

■ .NET 8 RC1

.NET 8 RC1 は Visual Studio のプレビュー版のアップデートでも入ります。素早くアップデートしましょう!