C#의 값 기반 특성

18568 단어 dotnetcsharpattributes

C Sharp의 값 기반 속성



This code is a snippet from the https://github.com/ZacharyPatten/Towel project.
Please check out the project if you want to see more code like it. :)



C#의 속성은 훌륭합니다. 런타임 시 동적으로 조회할 수 있는 코드 멤버에 메타데이터를 추가할 수 있습니다. 그러나 그것들은 또한 다루기에는 약간의 고통입니다. 일반적으로 System.Attribute를 상속하는 고유한 클래스를 만들고 리플렉션을 사용하여 속성을 조회하기 위해 적절한 상용구 코드를 추가해야 합니다. 다음은 고유한 속성을 만드는 예입니다.

using System;
using System.Linq;
using System.Reflection;

static class Program
{
    static void Main()
    {
        MyAttribute attribute = typeof(MyClass).GetCustomAttributes<MyAttribute>().FirstOrDefault();
        Console.WriteLine("MyCustomAttribute...");
        if (!(attribute is null))
        {
            Console.WriteLine("Found: " + true);
            Console.WriteLine("Value: " + attribute.Value);
        }
        else
        {
            Console.WriteLine("Found: " + false);
            Console.WriteLine("Value: " + null);
        }
    }
}

public class MyAttribute : Attribute
{
    public string Value { get; private set; }

    public MyAttribute(string value)
    {
        Value = value;
    }
}

[MyAttribute("hello world")]
public class MyClass { }


물론... 많은 상용구는 아니지만... 여전히 짜증나네요... 그래서 새로운 속성 유형을 정의하는 대신 상수 값을 사용할 수 있도록 값 기반 속성을 만들었습니다.

using System;
using Towel;

static class Program
{
    static void Main()
    {
        var (Found, Value) = typeof(MyClass).GetValueAttribute("MyCustomAttribute");
        Console.WriteLine("MyCustomAttribute...");
        Console.WriteLine("Found: " + Found);
        Console.WriteLine("Value: " + Value);
    }
}

[Value("MyCustomAttribute", "hello world")]
public class MyClass { }

ValueAttribute의 첫 번째 매개변수는 키와 같으며 두 번째 매개변수는
속성은 실제 값입니다. 그런 다음 키(첫 번째 매개변수)와 함께 GetValueAttribute 확장 메서드를 사용하면 자동으로 반영됩니다. 코드 멤버당 여러 개ValueAttributes를 추가할 수 있습니다.

[Value("Name", "Array Benchmarks")]
[Value("Description", "These benchmarks do...")]
[Value("Source URL", "google.com")]
public class MyBenchmarks
{
    // code...
}


어떻게 작동합니까? 첫 번째 예와 정확히 동일한 작업을 수행하지만
일치하는 키 값을 포함하는 ValueAttribute. 소스 코드는 다음과 같습니다.

using System;
using System.Reflection;

namespace Towel
{
    /// <summary>A value-based attribute.</summary>
    [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
    public class ValueAttribute : Attribute
    {
        internal object Attribute;
        internal object Value;

        /// <summary>Creates a new value-based attribute.</summary>
        /// <param name="attribute">The attribute.</param>
        /// <param name="value">The value.</param>
        public ValueAttribute(object attribute, object value)
        {
            Attribute = attribute;
            Value = value;
        }
    }

    /// <summary>Extension methods for reflection types and <see cref="ValueAttribute"/>.</summary>
    public static class ValueAttributeExtensions
    {
        /// <summary>Gets a <see cref="ValueAttribute"/> on a <see cref="MemberInfo"/>.</summary>
        /// <param name="memberInfo">The type to get the <see cref="ValueAttribute"/> of.</param>
        /// <param name="attribute">The attribute to get the value of.</param>
        /// <returns>
        /// (<see cref="bool"/> Found, <see cref="object"/> Value)
        /// <para>- <see cref="bool"/> Found: True if the attribute was found; False if not or if multiple attributes were found (ambiguous).</para>
        /// <para>- <see cref="object"/> Value: The value if found or default if not.</para>
        /// </returns>
        public static (bool Found, object Value) GetValueAttribute(this MemberInfo memberInfo, object attribute)
        {
            _ = memberInfo ?? throw new ArgumentNullException(nameof(memberInfo));
            bool found = false;
            object value = default;
            foreach (ValueAttribute valueAttribute in memberInfo.GetCustomAttributes<ValueAttribute>())
            {
                if (ReferenceEquals(attribute, valueAttribute.Attribute) || attribute.Equals(valueAttribute.Attribute))
                {
                    if (found)
                    {
                        return (false, default);
                    }
                    found = true;
                    value = valueAttribute.Value;
                }
            }
            return (found, value);
        }

        /// <summary>Gets a <see cref="ValueAttribute"/> on a <see cref="ParameterInfo"/>.</summary>
        /// <param name="parameterInfo">The type to get the <see cref="ValueAttribute"/> of.</param>
        /// <param name="attribute">The attribute to get the value of.</param>
        /// <returns>
        /// (<see cref="bool"/> Found, <see cref="object"/> Value)
        /// <para>- <see cref="bool"/> Found: True if the attribute was found; False if not or if multiple attributes were found (ambiguous).</para>
        /// <para>- <see cref="object"/> Value: The value if found or default if not.</para>
        /// </returns>
        public static (bool Found, object Value) GetValueAttribute(this ParameterInfo parameterInfo, object attribute)
        {
            _ = parameterInfo ?? throw new ArgumentNullException(nameof(parameterInfo));
            bool found = false;
            object value = default;
            foreach (ValueAttribute valueAttribute in parameterInfo.GetCustomAttributes<ValueAttribute>())
            {
                if (ReferenceEquals(attribute, valueAttribute.Attribute) || attribute.Equals(valueAttribute.Attribute))
                {
                    if (found)
                    {
                        return (false, default);
                    }
                    found = true;
                    value = valueAttribute.Value;
                }
            }
            return (found, value);
        }
    }
}

좋은 웹페이지 즐겨찾기