.NET 리플렉션을 사용하여 nullable 참조 유형을 확인하는 방법


15

C # 8.0에는 nullable 참조 형식이 도입되었습니다. nullable 속성을 가진 간단한 클래스는 다음과 같습니다.

public class Foo
{
    public String? Bar { get; set; }
}

리플렉션을 통해 클래스 속성에서 nullable 참조 유형을 사용하는지 확인하는 방법이 있습니까?


IL을 컴파일하고 살펴보면 이것이 유형 ( )에 추가 [NullableContext(2), Nullable((byte) 0)]되는 것처럼 보입니다. 그래서 확인해야 할 것이지만, 그것을 해석하는 방법의 규칙을 이해하려면 더 파고 들어야합니다! Foo
Marc Gravell

4
예, 그러나 사소한 것이 아닙니다. 다행히도 되고 문서화 .
Jeroen Mostert

아, 그렇습니다. 그래서 string? X어떤 특성을 얻을 수없고, string Y얻을 [Nullable((byte)2)]으로 [NullableContext(2)]접근 자에
마크 Gravell

1
형식 에 nullable (또는 nullable이 아닌) 포함되어 있으면 모두로 표시됩니다 NullableContext. 믹스가 있으면 Nullable사용됩니다. NullableContext어디에서나 방출하지 않아도되는 최적화 Nullable입니다.
canton7

답변:


11

이것은 적어도 테스트 한 유형에서 작동하는 것으로 보입니다.

당신은을 전달해야 PropertyInfo또한에 관심이있는 속성과 Type그 속성에 정의 된 ( 하지 파생 또는 부모 유형 - 그것은 정확한 유형이어야한다) :

public static bool IsNullable(Type enclosingType, PropertyInfo property)
{
    if (!enclosingType.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly).Contains(property))
        throw new ArgumentException("enclosingType must be the type which defines property");

    var nullable = property.CustomAttributes
        .FirstOrDefault(x => x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute");
    if (nullable != null && nullable.ConstructorArguments.Count == 1)
    {
        var attributeArgument = nullable.ConstructorArguments[0];
        if (attributeArgument.ArgumentType == typeof(byte[]))
        {
            var args = (ReadOnlyCollection<CustomAttributeTypedArgument>)attributeArgument.Value;
            if (args.Count > 0 && args[0].ArgumentType == typeof(byte))
            {
                return (byte)args[0].Value == 2;
            }
        }
        else if (attributeArgument.ArgumentType == typeof(byte))
        {
            return (byte)attributeArgument.Value == 2;
        }
    }

    var context = enclosingType.CustomAttributes
        .FirstOrDefault(x => x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableContextAttribute");
    if (context != null &&
        context.ConstructorArguments.Count == 1 &&
        context.ConstructorArguments[0].ArgumentType == typeof(byte))
    {
        return (byte)context.ConstructorArguments[0].Value == 2;
    }

    // Couldn't find a suitable attribute
    return false;
}

자세한 내용은 이 문서 를 참조하십시오.

일반적으로 요점은 속성 자체에 [Nullable]특성이 있거나 둘러싸는 유형이없는 경우 [NullableContext]특성 이있을 수 있다는 것입니다. 먼저를 [Nullable]찾은 다음 찾지 못하면 [NullableContext]둘러싸는 유형을 찾습니다 .

컴파일러는 속성을 어셈블리에 임베드 할 수 있으며 다른 어셈블리에서 유형을 볼 수 있으므로 리플렉션 전용로드를 수행해야합니다.

[Nullable]속성이 제네릭 인 경우 배열로 인스턴스화 될 수 있습니다. 이 경우 첫 번째 요소는 실제 속성을 나타내고 추가 요소는 일반 인수를 나타냅니다. [NullableContext]항상 단일 바이트로 인스턴스화됩니다.

값은 2"널링 가능" 을 의미합니다. 1"널링 가능하지 않음"을 0의미하고 "명백하지 않음"을 의미합니다.


정말 까다 롭습니다. 이 코드에서 다루지 않는 유스 케이스를 찾았습니다. 공용 인터페이스 IBusinessRelation : ICommon {}/ public interface ICommon { string? Name {get;set;} }. IBusinessRelation속성으로 메소드를 호출하면 Namefalse가 발생합니다.
gsharp

@gsharp 아, 인터페이스 나 상속으로 시도하지 않았습니다. 나는 (문맥에서의 모습 기본 인터페이스에서 속성) 상대적으로 쉽게 수정 같은데요 : 내가 시도하고 나중에 고칠거야
canton7을

1
더 큰. 방금 언급하고 싶었습니다. 이 nullable 물건은 나를 미치게합니다 ;-)
gsharp

1
,이 - @gsharp 그것을보고, 당신은 속성을 정의하는 인터페이스의 종류에 합격해야 ICommon하지를 IBusinessRelation. 각 인터페이스는 자체적으로 정의합니다 NullableContext. 내 대답을 명확히하고 이에 대한 런타임 검사를 추가했습니다.
canton7
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.