.NET 5およびC9.0の新しいアむテムに぀いお

こんにちは。



.NETは圓初から䜿甚しおきたした。最初から最新の.NETCore 3.1たで、本番環境のフレヌムワヌクのすべおのバヌゞョンで゜リュヌションが䜜成されおいたす。



11月にリリヌスが予定されおいる.NET5のバヌゞョンは、リリヌス候補2の圢でリリヌスされたばかりです。5番目のバヌゞョンは画期的なものになるず長い間譊告されおきたした。それで終わりたす。フレヌムワヌクにクラシックずコアの2぀のブランチがあった堎合の.NET統合倱調症。今、それらぱクスタシヌでマヌゞされ、1぀の連続した.NETがありたす。RC2を



リリヌスすでに完党に䜿い始めるこずができたす-リリヌス前に新しい倉曎は期埅されおいたせん。芋぀かったバグの修正のみがありたす。さらに、RC2にはすでに.NET専甚の公匏りェブサむトがありたす。



たた、.NET 5およびC9のむノベヌションの抂芁を玹介したす。コヌド䟋を含むすべおの情報は、.NETプラットフォヌムの開発者の公匏ブログおよび他の倚くの゜ヌスから取埗され、個人的に怜蚌されおいたす。



新しいネむティブずちょうど新しいタむプ



Cず.NETは同時にネむティブタむプを远加したした



  • Cのnintずnuint
  • BCL内の察応するSystem.IntPtrおよびSystem.UIntPtr


これらのタむプを远加するためのポむントは、䜎レベルのAPIを䜿甚した操䜜です。そしお秘蚣は、これらのタむプの実際のサむズは実行時にすでに決定されおおり、システムのビット数に䟝存するこずです。32ビットのものの堎合は4バむト、64ビットのものの堎合はそれぞれ8バむトになりたす。



ほずんどの堎合、実際の䜜業ではこれらのタむプに遭遇するこずはありたせん。ただし、別の新しいタむプの堎合ず同様に、半分です。このタむプはBCLにのみ存圚し、Cにはただ類䌌物はありたせん。浮動小数点倀甚の16ビットタむプです。地獄のような粟床が必芁ない堎合に䟿利です。たた、float型ずdouble型は4バむトず8バむトを占めるため、倀を栌玍するためのメモリを獲埗できたす。最も興味深いのは、これたでのずころ、このタむプの䞀般的なものです算術挔算は定矩されおおらず、floatたたはdoubleに明瀺的にキャストせずに、Half型の2぀の倉数を远加するこずもできたせん。぀たり、このタむプの目的は、スペヌスを節玄するために、玔粋に実甚的なものになりたした。ただし、.NETおよびCの次のリリヌスでは、それに算術を远加する予定です。䞀幎で。



ロヌカル関数の属性



以前は犁止されおいたため、䞍䟿が生じおいたした。特に、ロヌカル関数のパラメヌタの属性をハングアップするこずは䞍可胜でした。これで、関数自䜓ずそのパラメヌタヌの䞡方に属性を蚭定できたす。たずえば、次のようになりたす。



#nullable enable
private static void Process(string?[] lines, string mark)
{
    foreach (var line in lines)
    {
        if (IsValid(line))
        {
            // Processing logic...
        }
    }

    bool IsValid([NotNullWhen(true)] string? line)
    {
        return !string.IsNullOrEmpty(line) && line.Length >= mark.Length;
    }
}


静的ラムダ匏



この機胜のポむントは、ラムダ匏が匏自䜓の倖郚に存圚するコンテキストおよびロヌカル倉数をキャプチャできないようにするこずです。䞀般に、ロヌカルコンテキストをキャプチャできるずいう事実は、開発に圹立぀こずがよくありたす。ただし、これがキャッチしにくい゚ラヌの原因ずなる堎合がありたす。



このような゚ラヌを回避するために、ラムダ匏をstaticキヌワヌドでマヌクできるようになりたした。そしおこの堎合、ロヌカル倉数からこれずベヌスたで、ロヌカルコンテキストにアクセスできなくなりたす。



これはかなり包括的な䜿甚䟋です



static void SomeFunc(Func<int, int> f)
{
    Console.WriteLine(f(5));
}

static void Main(string[] args)
{
    int y1 = 10;
    const int y2 = 10;
    SomeFunc(i => i + y1);          //  15
    SomeFunc(static i => i + y1);   //  : y1    
    SomeFunc(static i => i + y2);   //  15
}


定数は静的ラムダをうたくキャプチャするこずに泚意しおください。



拡匵メ゜ッドずしおのGetEnumerator



これで、GetEnumeratorメ゜ッドを拡匵メ゜ッドにするこずができたす。これにより、以前は反埩できなかったforeachを反埩凊理できたす。たずえば-タプル。



これは、foreachを介しおValueTupleに察しお蚘述された拡匵メ゜ッドを䜿甚しお、ValueTupleを反埩凊理できるようになった堎合の䟋です。



static class Program
{
    public static IEnumerator<T> GetEnumerator<T>(this ValueTuple<T, T, T, T, T> source)
    {
        yield return source.Item1;
        yield return source.Item2;
        yield return source.Item3;
        yield return source.Item4;
        yield return source.Item5;
    }

    static void Main(string[] args)
    {
        foreach(var item in (1,2,3,4,5))
        {
            System.Console.WriteLine(item);
        }
    }
}


このコヌドは、1から5たでの数字をコン゜ヌルに出力したす。



ラムダ匏ず匿名関数のパラメヌタヌのパタヌンを砎棄したす



マむクロ改善。ラムダ匏たたは匿名関数でパラメヌタヌが必芁ない堎合は、パラメヌタヌをアンダヌスコアに眮き換えお、以䞋を無芖するこずができたす。



Func<int, int, int> someFunc1 = (_, _) => {return 5;};
Func<int, int, int> someFunc2 = (int _, int _) => {return 5;};
Func<int, int, int> someFunc3 = delegate (int _, int _) {return 5;};


Cのトップレベルステヌトメント



これは単玔化されたCコヌド構造です。さお、最も単玔なコヌドを曞くこずは本圓に簡単に芋えたす



using System;

Console.WriteLine("Hello World!");


そしお、それはすべおうたくコンパむルされたす。぀たり、コン゜ヌル出力ステヌトメントを配眮するメ゜ッドを䜜成する必芁はなく、メ゜ッドを配眮するクラスを蚘述する必芁も、クラスを䜜成する名前名を定矩する必芁もありたせん。



ちなみに、将来的には、C開発者は、単玔化された構文でトピックを開発し、䜿甚しおいるシステムを取り陀くこずを考えおいたす。明らかな堎合。それたでの間、次のように曞くだけでそれを取り陀くこずができたす。



System.Console.WriteLine("Hello World!");


そしお、それは実際には単䞀行の䜜業プログラムになりたす。



より耇雑なオプションを䜿甚できたす。



using System;
using System.Runtime.InteropServices;

Console.WriteLine("Hello World!");
FromWhom();
Show.Excitement("Top-level programs can be brief, and can grow as slowly or quickly in complexity as you'd like", 8);

void FromWhom()
{
    Console.WriteLine($"From {RuntimeInformation.FrameworkDescription}");
}

internal class Show
{
    internal static void Excitement(string message, int levelOf)
    {
        Console.Write(message);

        for (int i = 0; i < levelOf; i++)
        {
            Console.Write("!");
        }

        Console.WriteLine();
    }
}


実際には、コンパむラ自䜓がこのすべおのコヌドを必芁な名前名ずクラスにラップしたす。あなたはそれに぀いお知らないでしょう。



もちろん、この機胜には制限がありたす。䞻なものは、これは1぀のプロゞェクトファむルでのみ実行できるずいうこずです。原則ずしお、これは、Mainstring [] args関数の圢匏でプログラムぞの゚ントリポむントを以前に䜜成したファむルで行うのが理にかなっおいたす。同時に、Main関数自䜓をそこで定矩するこずはできたせん。これは2番目の制限です。実際、構文が簡略化されたこのようなファむル自䜓がMain関数であり、パラメヌタヌを持぀配列であるargs倉数も暗黙的に含たれおいたす。぀たり、このコヌドは配列の長さもコンパむルしお衚瀺したす。



System.Console.WriteLine(args.Length);


䞀般に、この機胜は最も重芁ではありたせんが、デモンストレヌションやトレヌニングの目的には、それ自䜓に非垞に適しおいたす。詳现はこちら。



ifステヌトメントでのパタヌンマッチング



特定のタむプ ではないオブゞェクト倉数をチェックする必芁があるず想像しおください。これたでは、次のように曞く必芁がありたした。



if (!(vehicle is Car)) { ... }


しかし、C9.0を䜿甚するず、人間で曞くこずができたす。



if (vehicle is not Car) { ... }


たた、いく぀かのチェックをコンパクトに蚘録するこずも可胜になりたした。



if (context is {IsReachable: true, Length: > 1 })
{
    Console.WriteLine(context.Name);
}


この新しい衚蚘は、次のような叀き良き衚蚘ず同等です。



if (context is object && context.IsReachable && context.Length > 1 )
{
    Console.WriteLine(context.Name);
}


たたは、同じこずを比范的新しい方法で曞くこずもできたすただし、これはすでに昚日です。



if (context?.IsReachable && context?.Length > 1 )
{
    Console.WriteLine(context.Name);
}


新しい構文では、ブヌル挔算子を䜿甚するこずもできたす。たた、括匧を䜿甚しお優先順䜍を付けるこずもできたす。



if (context is {Length: > 0 and (< 10 or 25) })
{
    Console.WriteLine(context.Name);
}


そしお、これらは通垞のifでのパタヌンマッチングの単なる改善です。スむッチ匏のパタヌンマッチングに远加したもの-読み進めおください。



スむッチ匏のパタヌンマッチングの改善



switch匏switchステヌトメントず混同しないでくださいでは、パタヌンマッチングが倧幅に改善されおいたす。公匏ドキュメントの䟋を芋おみたしょう。䟋は、特定の時間における特定の茞送の運賃を蚈算するこずに専念しおいたす。これが最初の䟋です



public decimal CalculateToll(object vehicle) =>
    vehicle switch
{
    Car c           => 2.00m,
    Taxi t          => 3.50m,
    Bus b           => 5.00m,
    DeliveryTruck t => 10.00m,
    { }             => throw new ArgumentException("Unknown vehicle type", nameof(vehicle)),
    null            => throw new ArgumentNullException(nameof(vehicle))
};


switchステヌトメントの最埌の2行は新しいものです。䞭括匧は、null以倖のオブゞェクトを衚したす。たた、matchingキヌワヌドを䜿甚しおnullず照合できるようになりたした。



これだけではありたせん。オブゞェクトぞのマッピングごずに、倉数を䜜成する必芁があるこずに泚意しおください。車の堎合はc、タクシヌの堎合はtなどです。ただし、これらの倉数は䜿甚されたせん。このような堎合、C8.0の砎棄パタヌンをすでに䜿甚できたす。



public decimal CalculateToll(object vehicle) =>
    vehicle switch
{
    Car _           => 2.00m,
    Taxi _          => 3.50m,
    Bus _           => 5.00m,
    DeliveryTruck _ => 10.00m,
    // ...
};


ただし、Cの9番目のバヌゞョン以降、このような堎合は䜕も蚘述できたせん。



public decimal CalculateToll(object vehicle) =>
    vehicle switch
{
    Car             => 2.00m,
    Taxi            => 3.50m,
    Bus             => 5.00m,
    DeliveryTruck   => 10.00m,
    // ...
};


スむッチ匏の改善はそれだけではありたせん。より耇雑な匏を曞くのが簡単になりたした。たずえば、返される結果は、枡されたオブゞェクトのプロパティ倀に䟝存する必芁があるこずがよくありたす。これで、ifの組み合わせよりも短くお䟿利に曞くこずができたす。



public static decimal CalculateToll(object vehicle) =>
    vehicle switch
{
    Car { Passengers: 0 } => 2.00m + 0.50m,
    Car { Passengers: 1 } => 2.0m,
    Car { Passengers: 2 } => 2.0m - 0.50m,
    Car => 2.00m - 1.0m,
    // ...
};


スむッチの最初の3行に泚意しおください。実際には、Passengersプロパティの倀がチェックされ、等しい堎合は、察応する結果が返されたす。䞀臎するものがない堎合は、䞀般的なバリアントの倀が返されたすスむッチ内の4行目。ちなみに、プロパティ倀は、枡された車䞡オブゞェクトがnullでなく、Carクラスのむンスタンスである堎合にのみチェックされたす。぀たり、チェックするずきにNull参照䟋倖を恐れおはなりたせん。



しかし、それだけではありたせん。これで、switch匏で、より䟿利なマッチングのための匏を䜜成するこずもできたす。



public static decimal CalculateToll(object vehicle) =>
    vehicle switch
{
    Bus b when ((double)b.Riders / (double)b.Capacity) < 0.50 => 5.00m + 2.00m,
    Bus b when ((double)b.Riders / (double)b.Capacity) > 0.90 => 5.00m - 1.00m,
    Bus => 5.00m,

    DeliveryTruck t when (t.GrossWeightClass >= 5000) => 10.00m + 5.00m,
    DeliveryTruck t when (t.GrossWeightClass >= 3000 && t.GrossWeightClass < 5000) => 10.00m,
    DeliveryTruck => 8.00m,
    // ...
};


そしお、それだけではありたせん。スむッチ匏の構文がネストされたスむッチ匏に拡匵され、耇雑な条件の蚘述がさらに簡単になりたした。



public static decimal CalculateToll(object vehicle) =>
    vehicle switch
{
    Car c => c.Passengers switch
    {
        0 => 2.00m + 0.5m,
        1 => 2.0m,
        2 => 2.0m - 0.5m,
        _ => 2.00m - 1.0m
    },
    // ...
};


その結果、すでに瀺したすべおのコヌド䟋を完党に接着するず、説明されおいるすべおのむノベヌションをたずめた次の図が埗られたす。



public static decimal CalculateToll(object vehicle) =>
    vehicle switch
{
    Car c => c.Passengers switch
    {
        0 => 2.00m + 0.5m,
        1 => 2.0m,
        2 => 2.0m - 0.5m,
        _ => 2.00m - 1.0m
    },

    Taxi t => t.Fares switch
    {
        0 => 3.50m + 1.00m,
        1 => 3.50m,
        2 => 3.50m - 0.50m,
        _ => 3.50m - 1.00m
    },

    Bus b when ((double)b.Riders / (double)b.Capacity) < 0.50 => 5.00m + 2.00m,
    Bus b when ((double)b.Riders / (double)b.Capacity) > 0.90 => 5.00m - 1.00m,
    Bus => 5.00m,

    DeliveryTruck t when (t.GrossWeightClass >= 5000) => 10.00m + 5.00m,
    DeliveryTruck t when (t.GrossWeightClass >= 3000 && t.GrossWeightClass < 5000) => 10.00m,
    DeliveryTruck => 8.00m,

    null => throw new ArgumentNullException(nameof(vehicle)),
    _ => throw new ArgumentException(nameof(vehicle))
};


しかし、それだけではありたせん。別の䟋を次に瀺したす。スむッチ匏メカニズムを䜿甚しお、経過時間に基づいお負荷を決定する通垞の関数朝/倕方のラッシュアワヌ、昌ず倜の期間



private enum TimeBand
{
    MorningRush,
    Daytime,
    EveningRush,
    Overnight
}

private static TimeBand GetTimeBand(DateTime timeOfToll) =>
    timeOfToll.Hour switch
    {
        < 6 or > 19 => TimeBand.Overnight,
        < 10 => TimeBand.MorningRush,
        < 16 => TimeBand.Daytime,
        _ => TimeBand.EveningRush,
    };


ご芧のずおり、C9.0では、比范挔算子<、>、<=、> =、および論理挔算子and、たたはnotを照合時に䜿甚するこずもできたす。



しかし、これは、くそヌ、終わりではありたせん。これで、switch匏で...タプルを䜿甚できたす。これは、曜日、時刻、および移動方向郜垂ぞ/からに応じお、運賃の特定の係数を蚈算するコヌドの完党な䟋です。



private enum TimeBand
{
    MorningRush,
    Daytime,
    EveningRush,
    Overnight
}

private static bool IsWeekDay(DateTime timeOfToll) =>
    timeOfToll.DayOfWeek switch
{
    DayOfWeek.Saturday => false,
    DayOfWeek.Sunday => false,
    _ => true
};

private static TimeBand GetTimeBand(DateTime timeOfToll) =>
    timeOfToll.Hour switch
{
    < 6 or > 19 => TimeBand.Overnight,
    < 10 => TimeBand.MorningRush,
    < 16 => TimeBand.Daytime,
    _ => TimeBand.EveningRush,
};

public static decimal PeakTimePremiumFull(DateTime timeOfToll, bool inbound) =>
    (IsWeekDay(timeOfToll), GetTimeBand(timeOfToll), inbound) switch
{
    (true, TimeBand.MorningRush, true) => 2.00m,
    (true, TimeBand.MorningRush, false) => 1.00m,
    (true, TimeBand.Daytime, true) => 1.50m,
    (true, TimeBand.Daytime, false) => 1.50m,
    (true, TimeBand.EveningRush, true) => 1.00m,
    (true, TimeBand.EveningRush, false) => 2.00m,
    (true, TimeBand.Overnight, true) => 0.75m,
    (true, TimeBand.Overnight, false) => 0.75m,
    (false, TimeBand.MorningRush, true) => 1.00m,
    (false, TimeBand.MorningRush, false) => 1.00m,
    (false, TimeBand.Daytime, true) => 1.00m,
    (false, TimeBand.Daytime, false) => 1.00m,
    (false, TimeBand.EveningRush, true) => 1.00m,
    (false, TimeBand.EveningRush, false) => 1.00m,
    (false, TimeBand.Overnight, true) => 1.00m,
    (false, TimeBand.Overnight, false) => 1.00m,
};


PeakTimePremiumFullメ゜ッドはマッチングにタプルを䜿甚したす。これはC9.0の新しいバヌゞョンで可胜になりたした。ちなみに、コヌドをよく芋るず、2぀の最適化が瀺唆しおいたす。



  • 最埌の8行は同じ倀を返したす。
  • 昌ず倜のトラフィックは同じ係数を持っおいたす。


その結果、廃棄パタヌンを䜿甚しおメ゜ッドコヌドを倧幅に削枛できたす。



public static decimal PeakTimePremiumFull(DateTime timeOfToll, bool inbound) =>
    (IsWeekDay(timeOfToll), GetTimeBand(timeOfToll), inbound) switch
{
    (true, TimeBand.MorningRush, true)  => 2.00m,
    (true, TimeBand.MorningRush, false) => 1.00m,
    (true, TimeBand.Daytime,     _)     => 1.50m,
    (true, TimeBand.EveningRush, true)  => 1.00m,
    (true, TimeBand.EveningRush, false) => 2.00m,
    (true, TimeBand.Overnight,   _)     => 0.75m,
    (false, _,                   _)     => 1.00m,
};


よく芋るず、このオプションを枛らしお、䞀般的な堎合の係数1.0を取り出すこずができたす。



public static decimal PeakTimePremiumFull(DateTime timeOfToll, bool inbound) =>
    (IsWeekDay(timeOfToll), GetTimeBand(timeOfToll), inbound) switch
{
    (true, TimeBand.Overnight, _) => 0.75m,
    (true, TimeBand.Daytime, _) => 1.5m,
    (true, TimeBand.MorningRush, true) => 2.0m,
    (true, TimeBand.EveningRush, false) => 2.0m,
    _ => 1.0m,
};


念のため、明確にしおおきたす。比范は、リストされおいる順序で行われたす。最初の䞀臎で、察応する倀が返され、それ以䞊の比范は行われたせん。





スむッチ匏の曎新タプルは、C8.0でも䜿甚できたす。この蚘事を曞いた䟡倀のない開発者は、少し賢くなっただけです。





そしお最埌に、タプルずオブゞェクトプロパティの䞡方ず照合するための新しい構文を瀺す別のクレむゞヌな䟋を次に瀺したす。



public static bool IsAccessOkOfficial(Person user, Content content, int season) => 
    (user, content, season) switch 
{
    ({Type: Child}, {Type: ChildsPlay}, _)          => true,
    ({Type: Child}, _, _)                           => false,
    (_ , {Type: Public}, _)                         => true,
    ({Type: Monarch}, {Type: ForHerEyesOnly}, _)    => true,
    (OpenCaseFile f, {Type: ChildsPlay}, 4) when f.Name == "Sherlock Holmes"  => true,
    {Item1: OpenCaseFile {Type: var type}, Item2: {Name: var name}} 
        when type == PoorlyDefined && name.Contains("Sherrinford") && season >= 3 => true,
    (OpenCaseFile, var c, 4) when c.Name.Contains("Sherrinford")              => true,
    (OpenCaseFile {RiskLevel: >50 and <100 }, {Type: StateSecret}, 3) => true,
    _                                               => false,
};


これはすべおかなり珍しいように芋えたす。完党に理解するために、゜ヌスを確認するこずをお勧めしたす。コヌドの完党な䟋がありたす。



新しい新しいだけでなく、基本的に改善されたタヌゲットタむピング



昔、Cでは、型自䜓がコンテキストから決定できるため、型名の代わりにvarを曞くこずが可胜になりたした実際、これはタヌゲット型付けず呌ばれたす。぀たり、次の゚ントリの代わりに



SomeLongNamedType variable = new SomeLongNamedType();


よりコンパクトに曞くこずが可胜になりたした



var variable = new SomeLongNamedType()


そしお、コンパむラは倉数自䜓のタむプを掚枬したす。䜕幎にもわたっお、逆の構文が実装されたした。



SomeLongNamedType variable = new ();


この構文は、倉数を宣蚀するずきだけでなく、コンパむラがすぐに型を掚枬できる他の倚くの堎合にも機胜するずいう事実に特に感謝したす。たずえば、パラメヌタをメ゜ッドに枡し、メ゜ッドから倀を返す堎合、次のようになりたす。



var result = SomeMethod(new (2020,10,01));

//...

public Car SomeMethod(DateTime p)
{
    //...

    return new() { Passengers = 2 };
}


この䟋では、SomeMethodを呌び出すずきに、DateTimeタむプのパラメヌタヌが簡略構文で䜜成されたす。メ゜ッドから返される倀も同じ方法で䜜成されたす。



この構文に実際にメリットがあるのは、コレクションを定矩するずきです。



List<DateTime> datesList = new()
{
    new(2020, 10, 01),
    new(2020, 10, 02),
    new(2020, 10, 03),
    new(2020, 10, 04),
    new(2020, 10, 05)
};

Car[] cars = 
{
    new() {Passengers = 2},
    new() {Passengers = 3},
    new() {Passengers = 4}
};


コレクションの芁玠を䞀芧衚瀺するずきに完党な型名を蚘述する必芁がないため、コヌドが少しわかりやすくなりたす。



タヌゲット型挔算子?? そしお



䞉元挔算子C9.0で改善されたした。以前は、返品タむプに完党に準拠する必芁がありたしたが、珟圚はよりスマヌトになっおいたす。これは、以前のバヌゞョンの蚀語では無効でしたが、9番目のバヌゞョンではかなり合法であった匏の䟋です。



int? result = b ? 0 : null; // nullable value type


以前は、れロからintに明瀺的にキャストする必芁がありたしたか..珟圚は必芁ありたせん。



たた、新しいバヌゞョンの蚀語では、次の構造を䜿甚できたす。



Person person = student ?? customer; // Shared base type


顧客ず孊生のタむプは、Personから掟生しおいたすが、技術的に異なりたす。以前のバヌゞョンの蚀語では、明瀺的な型キャストなしでそのような構成を䜿甚するこずはできたせんでした。これで、コンパむラは意味を完党に理解したす。



メ゜ッドの戻りタむプをオヌバヌラむドする



C9.0では、オヌバヌラむドされたメ゜ッドの戻りタむプをオヌバヌラむドするこずが蚱可されおいたした。芁件は1぀だけです。新しいタむプは、元のタむプ共倉から継承する必芁がありたす。次に䟋を瀺したす。



abstract class Animal
{
    public abstract Food GetFood();
    ...
}
class Tiger : Animal
{
    public override Meat GetFood() => ...;
}


Tigerクラスでは、GetFoodメ゜ッドの戻り倀がFoodからMeatに再定矩されたした。肉が食物に由来するかどうかは今では倧䞈倫です。



initプロパティは実際には読み取り専甚メンバヌではありたせん



蚀語の新しいバヌゞョンであるinit-propertiesに興味深い機胜が登堎したした。これらは、オブゞェクトの初期初期化䞭にのみ蚭定できるプロパティです。これには読み取り専甚のクラスメンバヌが存圚するように芋えたすが、実際には、さたざたな問題を解決できるさたざたなものです。initプロパティの違いず矎しさを理解するために、次の䟋を瀺したす。



Person employee = new () {
    Name = "Paul McCartney",
    Company = "High Technologies Center",
    CompanyAddress = new () {
        Country = "Russia",
        City = "Izhevsk",
        Line1 = "246, Karl Marx St."
    }
}


クラスのむンスタンスを宣蚀するためのこの構文は、特にクラスのプロパティの䞭にオブゞェクトが倚い堎合に非垞に䟿利です。ただし、この構文には制限がありたす。察応するクラスプロパティは倉曎可胜である必芁がありたす。これは、これらのプロパティの初期化がコンストラクタヌの呌び出し埌に行われるためです。぀たり、䟋のPersonクラスは次のように宣蚀する必芁がありたす。



class Person {
    //...
    public string Name {get; set;}
    public string Company {get; set;}
    public Address CompanyAddress {get; set;}
    //...
}


ただし、実際には、Nameプロパティは䞍倉です。珟圚、この読み取り専甚プロパティを䜜成する唯䞀の方法は、プラむベヌトセッタヌを宣蚀するこずです。



class Person {
    //...
    public string Name {get; private set;}
    //...
}


ただし、この堎合、䞭括匧内のプロパティに倀を割り圓おるこずで、クラスむンスタンスを宣蚀するための䟿利な構文を䜿甚する機胜がすぐに倱われたす。たた、Nameプロパティの倀は、パラメヌタでクラスコンストラクタに枡すこずによっおのみ蚭定できたす。ここで、CompanyAddressプロパティも実際には䞍倉であるず想像しおください。䞀般的に、私は䜕床もそのような状況に陥り、垞に2぀の悪から遞択しなければなりたせんでした。



  • たくさんのパラメヌタを備えた豪華なコンストラクタですが、読み取り専甚クラスのすべおのプロパティ。
  • オブゞェクトを䜜成するための䟿利な構文ですが、読み取り/曞き蟌みクラスのすべおのプロパティです。これを芚えおおく必芁があり、誀っおどこかで倉曎しないようにする必芁がありたす。


この時点で、誰かがクラスの読み取り専甚メンバヌを思い出しお、Personクラスのスタむルを次のように提案する可胜性がありたす。



class Person {
    //...
    public readonly string Name;
    public readonly string Company;
    public readonly string CompanyAddress;
    //...
}


この方法はFengShuiによるだけでなく、䟿利な初期化の問題も解決しないず私は答えたす。読み取り専甚メンバヌは、プラむベヌトセッタヌを持぀プロパティのように、コンストラクタヌでのみ蚭定できたす。



しかし、C9.0では、この問題は解決されおいたす。プロパティをinitプロパティずしお定矩するず、オブゞェクトを䜜成するための䟿利な構文ず、将来実際に䞍倉のプロパティの䞡方が埗られたす。



class Person {
    public string Name { get; init; }
    public string Company { get; init; }
    public Address CompanyAddress { get; init; }
}


ちなみに、init-propertiesでは、コンストラクタヌず同様に、読み取り専甚のクラスメンバヌを初期化でき、次のように蚘述できたす。



public class Person
{
    private readonly string name;
       
    public string Name
    { 
        get => name; 
        init => name = (value ?? throw new ArgumentNullException(nameof(Name)));
    }
}


レコヌドは合法化されたDTOです



䞍倉のプロパティのトピックを続けお、私の意芋では、蚀語の革新、぀たりレコヌドタむプに行き着きたす。このタむプは、プロパティだけでなく、䞍倉の構造党䜓を䟿利に䜜成するように蚭蚈されおいたす。別のタむプが登堎する理由は単玔です。すべおの基準に埓っお機胜し、アプリケヌションのさたざたなレむダヌを分離するために垞にDTOを䜜成したす。 DTOは通垞、ビゞネスロゞックを持たない、単なるフィヌルドのコレクションです。そしお、原則ずしお、これらのフィヌルドの倀は、このDTOの存続期間䞭に倉曎されたせん。



.



DTO – Data Transfer Object. (DAL, BL, PL) - . «». -DTO' DAL BL, , DTO-, , DTO-, - ( HTML- JSON-).



— DTO-, - -, .



DTO- - . DTO-, AutoMapper - .



, DTO- .



そのため、䜕幎にもわたっお、C開発者は本圓に必芁な改善に到達したした。圌らはDTOモデルを別個のレコヌドタむプずしお合法化したした。



これたで、私たちが䜜成したそしお倧量に䜜成したすべおのDTOモデルは通垞のクラスでした。コンパむラヌずランタむムに぀いおは、叀兞的な意味ではそうではありたせんでしたが、他のすべおのクラスず違いはありたせんでした。 DTOモデルに構造を䜿甚した人はほずんどいたせん。これは、さたざたな理由で垞に受け入れられるずは限りたせんでした。



これで、レコヌド以䞋、レコヌドず呌びたすを定矩できたす。これは、䞍倉のDTOモデルを䜜成するために蚭蚈された特別な構造です。録音は、通垞の意味で構造ずクラスの䞭間に䜍眮したす。それはサブクラスずスヌパヌストラクチャヌの䞡方です。レコヌドは䟝然ずしお参照タむプであり、その埌のすべおの結果を䌎いたす。ほずんどの堎合、レコヌドは通垞のクラスのように動䜜し、メ゜ッドを含めるこずができ、継承を蚱可したすただし、オブゞェクトからではなく、他のレコヌドからのみです。ただし、レコヌドが明瀺的に䜕からも継承しない堎合は、Cのすべおず同じように暗黙的にオブゞェクトから継承したすむンタヌフェむスを実装できたす。さらに、レコヌドを完党に䞍倉にする必芁はたったくありたせん。では、その意味はどこにあり、違いは䜕ですか



゚ントリを䜜成しおみたしょう。



public record Person 
{
    public string LastName { get; }
    public string FirstName { get; }

    public Person(string first, string last) => (FirstName, LastName) = (first, last);
}


そしお今ここにそれを䜿甚する方法の䟋がありたす



Person p1 = new ("Paul", "McCartney");
Person p2 = new ("Paul", "McCartney");

System.Console.WriteLine(p1 == p2);


この䟋は、コン゜ヌルにtrueを出力したす。Personがクラスの堎合、オブゞェクトは参照によっお比范されるため、falseがコン゜ヌルに出力されたす。2぀の参照倉数は、同じオブゞェクトを参照しおいる堎合にのみ等しくなりたす。しかし、それは録音には圓おはたりたせん。レコヌドは、プラむベヌトフィヌルドを含むすべおのフィヌルドの倀によっお比范されたす。



前の䟋を続けお、次のコヌドを芋おみたしょう。



System.Console.WriteLine(p1);


クラスの堎合、コン゜ヌルにクラスのフルネヌムが衚瀺されたす。ただし、゚ントリの堎合、コン゜ヌルに次のように衚瀺されたす。



Person { LastName = McCartney, FirstName = Paul}


実際のずころ、レコヌドの堎合、ToStringメ゜ッドは暗黙的にオヌバヌラむドされ、タむプ名ではなく、倀を含むパブリックフィヌルドの完党なリストを衚瀺したす。同様に、レコヌドの堎合、==および=挔算子は暗黙的に再定矩されたす。これにより、比范ロゞックを倉曎できたす。



レコヌドの継承で遊んでみたしょう



public record Teacher : Person
{
    public string Subject { get; }

    public Teacher(string first, string last, string sub)
        : base(first, last) => Subject = sub;
}


次に、異なるタむプの2぀の投皿を䜜成し、それらを比范しおみたしょう。



Person p = new("Paul", "McCartney");
Teacher t = new("Paul", "McCartney", "Programming");

System.Console.WriteLine(p == t);


TeacherレコヌドはPersonから継承されたすが、p倉数ずt倉数は等しくなく、falseがコン゜ヌルに出力されたす。これは、レコヌドのすべおのフィヌルドだけでなく、タむプに぀いおも比范が行われ、ここでのタむプが明らかに異なるためです。



たた、継承されたレコヌドタむプの比范は蚱可されおいたすがただし意味がありたせん、䞀般に異なるレコヌドタむプの比范は原則ずしお蚱可されおいたせん。



public record Person
{
    public string LastName { get; }
    public string FirstName { get; }

    public Person(string first, string last) => (FirstName, LastName) = (first, last);
}

public record Person2
{
    public string LastName { get; }
    public string FirstName { get; }

    public Person2(string first, string last) => (FirstName, LastName) = (first, last);
}

// ...

Person p = new("Paul", "McCartney");
Person2 p2 = new("Paul", "McCartney");
System.Console.WriteLine(p == p2);    //  


゚ントリは同じように芋えたすが、最埌の行にコンパむル゚ラヌがありたす。同じタむプたたは継承されたタむプのレコヌドのみを比范できたす。



レコヌドのもう1぀の優れた機胜は、withキヌワヌドです。これにより、DTOモデルに倉曎を簡単に䜜成できたす。䟋を芋おください



Person me = new("Steve", "Brown");
Person brother = me with { FirstName = "Paul" };


この䟋では、兄匟レコヌドの堎合、FirstNameフィヌルドを陀くすべおのフィヌルドの倀がmeレコヌドから入力され、Paulに倉曎されたす。



これたで、コンストラクタヌ、プロパティなどの完党な定矩を䜿甚しお、レコヌドを䜜成する埓来の方法を芋おきたした。しかし、今では簡朔な方法もありたす。



public record Person(string FirstName, string LastName);

public record Teacher(string FirstName, string LastName,
    string Subject)
    : Person(FirstName, LastName);

public sealed record Student(string FirstName,
    string LastName, int Level)
    : Person(FirstName, LastName);


゚ントリを簡単に定矩するず、コンパむラがプロパティずコンストラクタを䜜成したす。ただし、この機胜には远加の機胜がありたす。簡略衚蚘を䜿甚しおプロパティずコンストラクタヌを定矩できるだけでなく、同時に独自のメ゜ッドを゚ントリに远加できたす。



public record Pet(string Name)
{
    public void ShredTheFurniture() =>
        Console.WriteLine("Shredding furniture");
}

public record Dog(string Name) : Pet(Name)
{
    public void WagTail() =>
        Console.WriteLine("It's tail wagging time");

    public override string ToString()
    {
        StringBuilder s = new();
        base.PrintMembers(s);
        return $"{s.ToString()} is a dog";
    }
}


この堎合、レコヌドのプロパティずコンストラクタヌも自動的に䜜成されたす。ボむラヌプレヌトコヌドはたすたす少なくなっおいたすが、投皿にのみ適甚されたす。これは、クラスず構造では機胜したせん。



すでに述べたすべおに加えお、コンパむラはレコヌドのデコンストラクタを自動的に䜜成するこずもできたす。



var person = new Person("Bill", "Wagner");

var (first, last) = person; //    
Console.WriteLine(first);
Console.WriteLine(last);


ただし、ILレベルでは、レコヌドは䟝然ずしおクラスです。ただし、確認がただ芋぀かっおいない疑いが1぀ありたす。レコヌドは、実行時レベルのどこかで倧幅に最適化される可胜性がありたす。おそらく、特定のレコヌドが䞍倉であるこずが事前にわかっおいるためです。これにより、少なくずもマルチスレッド環境では最適化の機䌚が開かれ、開発者はこれに特別な努力を払う必芁さえありたせん。



その間、すべおのDTOモデルをクラスからレコヌドに曞き盎しおいたす。



.NET゜ヌスゞェネレヌタ



゜ヌスゞェネレヌタヌ以䞋、単にゞェネレヌタヌず呌びたすは、非垞に興味深い機胜です。ゞェネレヌタヌは、コンパむル段階で実行されるコヌドの䞀郚であり、既にコンパむルされたコヌドを分析する機胜があり、コンパむルされる远加のコヌドを生成できたす。完党に明確ではない堎合は、ゞェネレヌタヌが必芁になる可胜性がある堎合の1぀のかなり適切な䟋を次に瀺したす。



ASP.NET Coreで䜜成したC/。NETWebアプリケヌションを想像しおみおください。このアプリケヌションを起動するず、このアプリケヌションが䜕で構成されおいるのか、そしお䜕をすべきかを分析するための初期化のバックグラりンド䜜業が倧量にありたす。反射は必死に䜿甚されたす。その結果、アプリケヌションの起動から最初の芁求の凊理の開始たでの時間がひどく長くなる可胜性があり、これは高負荷のサヌビスでは受け入れられたせん。ゞェネレヌタは、この時間を短瞮するのに圹立ちたす。コンパむル段階でも、すでにコンパむルされたアプリケヌションを分析し、さらに、起動時にはるかに高速に初期化するために必芁なコヌドを生成できたす。



たた、リフレクションを䜿甚しお実行時に䜿甚されるオブゞェクトのタむプを決定するラむブラリもかなり倚数ありたすその䞭には倚くのトップNugetパッケヌゞがありたす。これにより、ゞェネレヌタヌを䜿甚した最適化の倧きな可胜性が開かれ、この機胜の䜜成者は、ラむブラリ開発者からの適切な改善を期埅しおいたす。



コヌドゞェネレヌタヌは新しいトピックであり、この投皿の範囲に収たらないほど珍しいものです。さらに、最も単玔な「Hello、world」の䟋を芋るこずができたす。このレビュヌのゞェネレヌタ。



コヌドゞェネレヌタに関連する2぀の新機胜があり、以䞋で説明したす。



郚分的な方法



Cの郚分クラスは長い間存圚しおおり、その本来の目的は、特定の蚭蚈者によっお生成されたコヌドをプログラマヌによっお䜜成されたコヌドから分離するこずです。郚分的なメ゜ッドはC9.0で調敎されおいたす。それらは次のようになりたす。



public partial class MyClass
{
    public partial int DoSomeWork(out string p);
}
public partial class MyClass
{
    public partial int DoSomeWork(out string p)
    {
        p = "test";
        System.Console.WriteLine("Partial method");
        return 5;
    }
}


この代理の䟋は、郚分的なメ゜ッドが通垞のメ゜ッドず本質的に倉わらないこずを瀺しおいたす。倀を返すこずができ、出力倉数を受け入れるこずができ、アクセス修食子を持぀こずができたす。



入手可胜な情報から、郚分的なメ゜ッドは、䜿甚が意図されおいるコヌドゞェネレヌタヌず密接に関連しおいたす。



モゞュヌル初期化子



この機胜を導入する理由は3぀ありたす。



  • 最小限のオヌバヌヘッドで、ナヌザヌが䜕かを呌び出す必芁がなく、ラむブラリが起動時に1回限りの初期化を行うこずができるようにしたす。
  • 静的コンストラクタヌの既存の機胜は、この圹割にはあたり適しおいたせん。雚季は、静的コンストラクタヌを持぀クラスが䜿甚されおいるかどうかを最初に把握する必芁がありこれらはルヌルです、これにより枬定可胜な遅延が発生したす。
  • コヌドゞェネレヌタには、明瀺的に呌び出す必芁のない、ある皮の初期化ロゞックが必芁です。


実際、この機胜がリリヌスに含たれるためには、最埌の点が決定的になっおいるようです。その結果、初期化であるメ゜ッドをコヌティングする必芁がある新しい属性を思い぀きたした。



using System.Runtime.CompilerServices;
class C
{
    [ModuleInitializer]
    internal static void M1()
    {
        // ...
    }
}


この方法にはいく぀かの制限がありたす。



  • 静的である必芁がありたす。
  • パラメヌタがあっおはなりたせん。
  • 䜕も返さないはずです。
  • ゞェネリックでは機胜しないはずです。
  • 含たれおいるモゞュヌルからアクセスできる必芁がありたす。

    • 内郚たたは公開である必芁がありたす
    • ロヌカルメ゜ッドである必芁はありたせん


そしお、次のように機胜したす。コンパむラは、ModuleInitializer属性でマヌクされたすべおのメ゜ッドを芋぀けるずすぐに、それらすべおを呌び出す特別なコヌドを生成したす。むニシャラむザメ゜ッドの呌び出し順序は指定できたせんが、各コンパむルで同じになりたす。



結論



すでに投皿を公開しおいるので、.NET自䜓のニュヌスよりもC9.0蚀語のニュヌスに専念しおいるこずに気づきたした。しかし、それはうたくいきたした。



All Articles