ufcpp / UfcppSample

http://ufcpp.net/ 向けのサンプル
Apache License 2.0
134 stars 40 forks source link

Extensions #471

Open ufcpp opened 3 months ago

ufcpp commented 3 months ago

https://ufcpp.net/blog/2024/3/extensions/

ufcpp commented 3 months ago

昔ちょこっと書いたの:

要求1: 静的メンバー

C# 3.0 の拡張メソッドはインスタンス メソッドでだけ「既存の型に追加したように見える書き方」ができます。 123.Extension() とはできても、int.Extension() とはできません。

既存の型に静的メソッドを足せなくて困る例としては、ポリフィル(古い環境向けに最新機能を移植して使えるようにするような用途)とかでしょうか。

例えば割かし最近(.NET 8、2023年11月正式リリース)、TimeProviderという型が入って以下のような書き方ができるようになりました。

TimeProvider tp = TimeProvider.System;

// 1秒待つ。
// TimeProvider を差し替えて、単体テストとかでは一瞬で終わるようにしたい。
await Task.Delay(TimeSpan.FromSeconds(1), tp);

TimeProvider クラス自体は、.NET 8 より前のランタイムでも使えるようにできます。 (実際、Microsoft.Bcl.TimeProvider という NuGet パッケージが公式提供されています。) 以下のような書き方で、「なければ追加、あれば type forward」みたいなことができます。

#if NET8_0_OR_GREATER

using System;
using System.Runtime.CompilerServices;

[assembly: TypeForwardedTo(typeof(TimeProvider))]

#else

namespace System;

public class TimeProvider
{
    // 互換実装
}

#endif

インスタンス メソッドの追加風のことも、拡張メソッドでできました。

using System.Runtime.CompilerServices;

namespace System.Threading.Tasks;

public static class TaskExtensions
{
    // GetAwaiter メソッドがあると C# 5.0 の await が使える。
    public static TaskAwaiter GetAwaiter(this Task task)
    {
        // 今はインスタンス メソッドで GetAwaiter があるけど、
        // .NET Framework 4.0 以前はなかったので自作が必要。
        return task.GetAwaiter();
    }
}

ですが、先ほどの例に出てきた Task.Delay(TimeSpan, TimeProvider) は既存の型への静的メソッドの追加なので、 これまでの C# ではどうやってもできませんでした。

ufcpp commented 2 months ago

13から外れた。 Unsafe.As 路線は JIT を混乱させて文字通り安全じゃないらしく、急遽路線変更。