はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【Unity】UnityでCallerArgumentExpression属性, required修飾子, initアクセサを利用する方法(自分で定義する必要があり)

はじめに

現在のUnityで利用できるC#のバージョンはC#9までになってます。ただ少し裏技?を使うことで実質C#11まで利用することが可能になっています。

zenn.dev

ただ注意点としてはasmdefが絡んでくるとよく知られているCsprojModifierを用いる手法だけだといけなかったはずです。(CsprojModifierでやっていることはAssetPostprocessor.OnGeneratedCSProjectを用いてcsprojに変更を加える)

github.com

本題のCallerArgumentExpressionなのですが、C#10から登場した属性です。Unityに対してCsprojModifierなどを用いてC#のバージョンをあげたとしても、定義がないと怒られてしまいます。

定義がないと怒られる

やり方

以下を自前で定義してあげればOKです。

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
    public sealed class CallerArgumentExpressionAttribute : Attribute
    {
        public CallerArgumentExpressionAttribute(string parameterName)
        {
            ParameterName = parameterName;
        }

        public string ParameterName { get; }
    }
}

runtime/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CallerArgumentExpressionAttribute.cs at main · dotnet/runtime · GitHub

実験

using System.Runtime.CompilerServices;
using UnityEngine;

public class Sample : MonoBehaviour
{
    private void Start()
    {
        Hoge("テキスト");
    }

    private static void Hoge(string text,
        [CallerArgumentExpression("text")] string? textExpression = null)
    {
        // テキスト
        Debug.Log(text);
        // "テキスト"
        Debug.Log(textExpression);
    }
}

ちなみに

CallerArgumentExpressionを主に取り上げましたが、required修飾子やinitアクセサを利用したい場合も同様に定義を自分でしてあげる必要があります。

// 以下のように required を利用したい, initを利用したい場合は
public class Person
{
    public required string Name { get; init; }
    public int? Age { get; init; }
}

requiredを利用するために

namespace System.Runtime.CompilerServices
{
    /// <summary>
    /// Indicates that compiler support for a particular feature is required for the location where this attribute is applied.
    /// </summary>
    [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
    public sealed class CompilerFeatureRequiredAttribute : Attribute
    {
        public CompilerFeatureRequiredAttribute(string featureName)
        {
            FeatureName = featureName;
        }

        /// <summary>
        /// The name of the compiler feature.
        /// </summary>
        public string FeatureName { get; }

        /// <summary>
        /// If true, the compiler can choose to allow access to the location where this attribute is applied if it does not understand <see cref="FeatureName"/>.
        /// </summary>
        public bool IsOptional { get; init; }

        /// <summary>
        /// The <see cref="FeatureName"/> used for the ref structs C# feature.
        /// </summary>
        public const string RefStructs = nameof(RefStructs);

        /// <summary>
        /// The <see cref="FeatureName"/> used for the required members C# feature.
        /// </summary>
        public const string RequiredMembers = nameof(RequiredMembers);
    }
}

runtime/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CompilerFeatureRequiredAttribute.cs at main · dotnet/runtime · GitHub

using System.ComponentModel;

namespace System.Runtime.CompilerServices
{
    /// <summary>Specifies that a type has required members or that a member is required.</summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class RequiredMemberAttribute : Attribute
    { }
}

runtime/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RequiredMemberAttribute.cs at main · dotnet/runtime · GitHub

initを利用するために

namespace System.Runtime.CompilerServices
{
    internal sealed class IsExternalInit { }
}

runtime/src/tasks/Common/IsExternalInit.cs at main · dotnet/runtime · GitHub