MRが楽しい

MRやVRについて学習したことを書き残す

基底クラスでwith式を使ったインスタンス生成の動きを確認する

本日は C# の調査枠です。
基底クラスでwith式を使ったインスタンス生成の動きを確認したので記事に残します。

基底クラスでwith式を使ったインスタンス生成を行う

以下のような基底クラスと継承クラスを作成しました。
各クラスには自身のメソッドとして with 式を利用して一部のステータスを変更して新しいインスタンスを返すメソッドを実装しています。
・ContentStatus.cs(基底クラス)

using System;
using UnityEngine;

[Serializable]
public abstract record ContentStatus
{
    [SerializeField] private ContentStatusType _contentStatusType;

    public ContentStatus(ContentStatusType contentStatusType)
    {
        _contentStatusType = contentStatusType;
    }

    public ContentStatusType GetContentStatusType() => _contentStatusType;

    public ContentStatus WithContentStatusType(ContentStatusType contentStatusType)
    {
        ContentStatus status = this with { _contentStatusType = contentStatusType };
        return status;
    }
}

public enum ContentStatusType
{
    Food,
    Toy,
}

・ArtifactContentStatus.cs(継承した基底クラス)

using System;
using UnityEngine;

[Serializable]
public abstract record ArtifactContentStatus : ContentStatus
{
    [SerializeField] private ArtifactContentStatusType _artifactcContentStatusType;

    public ArtifactContentStatus(
        ArtifactContentStatusType artifactcContentStatusType,
        ContentStatusType contentStatusType) :
        base(contentStatusType)
    {
        _artifactcContentStatusType = artifactcContentStatusType;
    }

    public ArtifactContentStatusType GetArtifactContentStatusType() => _artifactcContentStatusType;

    public ArtifactContentStatus WithArtifactContentStatusType(ArtifactContentStatusType artifactcContentStatusType)
    {
        ArtifactContentStatus status = this with { _artifactcContentStatusType = artifactcContentStatusType };
        return status;
    }
}

public enum ArtifactContentStatusType
{
    Free,
    Hold,
}

・HolomonArtifactContentStatus.cs(継承クラス)

using System;
using UnityEngine;

[Serializable]
public record HolomonArtifactContentStatus : ArtifactContentStatus
{
    [SerializeField] private HolomonArtifactContentStatusType _holomonArtifactContentStatusType;

    public HolomonArtifactContentStatus(
        HolomonArtifactContentStatusType holomonArtifactContentStatusType,
        ArtifactContentStatusType artifactContentStatusType,
        ContentStatusType contentStatusType) :
        base(artifactContentStatusType, contentStatusType)
    {
        _holomonArtifactContentStatusType = holomonArtifactContentStatusType;
    }

    public HolomonArtifactContentStatusType GetHolomonArtifactContentStatusType() => _holomonArtifactContentStatusType;

    public HolomonArtifactContentStatus WithHolomonArtifactContentStatusType
        (HolomonArtifactContentStatusType holomonArtifactContentStatusType)
    {
        HolomonArtifactContentStatus status = this with
            { _holomonArtifactContentStatusType = holomonArtifactContentStatusType };
        return status;
    }
}

public enum HolomonArtifactContentStatusType
{
    Like,
    Hate,
}

更に以下のサンプルスクリプトを作成し、以下のように基底クラス内で with を使ったインスタンス生成をした場合に継承クラスの情報が保持できるかを確認しました。
・InheritedWithTest.cs

using UnityEngine;

public class InheritedWithTest : MonoBehaviour
{
    void Start()
    {
        Test1();
        Test2();
        Test3();
    }

    void Test1()
    {
        ContentStatus status = new HolomonArtifactContentStatus(
            HolomonArtifactContentStatusType.Like,
            ArtifactContentStatusType.Free,
            ContentStatusType.Food);

        status = ((ArtifactContentStatus)status).WithArtifactContentStatusType(ArtifactContentStatusType.Hold);

        HolomonArtifactContentStatus holomonStatus = (HolomonArtifactContentStatus)status;
        Debug.Log($"{holomonStatus.GetContentStatusType()}, " + 
                  $"{holomonStatus.GetArtifactContentStatusType()}, " + 
                  $"{holomonStatus.GetHolomonArtifactContentStatusType()}");
    }
    
    void Test2()
    {
        ContentStatus status = new HolomonArtifactContentStatus(
            HolomonArtifactContentStatusType.Hate,
            ArtifactContentStatusType.Hold,
            ContentStatusType.Toy);

        status = status.WithContentStatusType(ContentStatusType.Food);

        HolomonArtifactContentStatus holomonStatus = (HolomonArtifactContentStatus)status;
        Debug.Log($"{holomonStatus.GetContentStatusType()}, " + 
                  $"{holomonStatus.GetArtifactContentStatusType()}, " + 
                  $"{holomonStatus.GetHolomonArtifactContentStatusType()}");
    }
    
    
    void Test3()
    {
        ContentStatus status = new HolomonArtifactContentStatus(
            HolomonArtifactContentStatusType.Like,
            ArtifactContentStatusType.Free,
            ContentStatusType.Food);

        status = status.WithContentStatusType(ContentStatusType.Toy);
        status = ((ArtifactContentStatus)status).WithArtifactContentStatusType(ArtifactContentStatusType.Hold);
        status = ((HolomonArtifactContentStatus)status).WithHolomonArtifactContentStatusType(
            HolomonArtifactContentStatusType.Hate);

        HolomonArtifactContentStatus holomonStatus = (HolomonArtifactContentStatus)status;
        Debug.Log($"{holomonStatus.GetContentStatusType()}, " + 
                  $"{holomonStatus.GetArtifactContentStatusType()}, " + 
                  $"{holomonStatus.GetHolomonArtifactContentStatusType()}");
    }
}

結果としては継承クラスのインスタンスの情報もそのままに with 式で新しいインスタンスが生成されるようです。
メソッドの返す型が基底クラスになるため、情報が欠けてしまわないか懸念しましたがこのような with 式の使い方も特に問題ないようです。