Cách sử dụng phản chiếu .NET để kiểm tra loại tham chiếu nullable


15

C # 8.0 giới thiệu các loại tham chiếu nullable. Đây là một lớp đơn giản với thuộc tính nullable:

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

Có cách nào để kiểm tra một thuộc tính lớp sử dụng kiểu tham chiếu nullable thông qua sự phản chiếu không?


biên dịch và nhìn vào IL, có vẻ như điều này thêm [NullableContext(2), Nullable((byte) 0)]vào loại ( Foo) - vì vậy đó là những gì cần kiểm tra, nhưng tôi cần phải đào sâu hơn để hiểu các quy tắc về cách diễn giải điều đó!
Marc Gravell

4
Vâng, nhưng nó không tầm thường. May mắn thay, nó được ghi lại .
Jeroen Mostert

ah tôi thấy; vì vậy string? Xkhông có thuộc tính và string Ycó được [Nullable((byte)2)]với [NullableContext(2)]những người truy cập
Marc Gravell

1
Nếu một loại chỉ chứa nullables (hoặc không nullables), thì đó là tất cả đại diện bởi NullableContext. Nếu có một hỗn hợp, sau đó Nullablesử dụng là tốt. NullableContextlà một tối ưu hóa để thử và tránh phải phát ra Nullablekhắp nơi.
cant7

Câu trả lời:


11

Điều này dường như hoạt động, ít nhất là trên các loại tôi đã thử nghiệm nó.

Bạn cần chuyển PropertyInfocho thuộc tính mà bạn quan tâm và cả thuộc tính Typemà tài sản đó được xác định ( không phải là loại có nguồn gốc hoặc loại gốc - nó phải là loại chính xác):

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;
}

Xem tài liệu này để biết chi tiết.

Ý chính chung là bản thân [Nullable]thuộc tính có thể có một thuộc tính trên nó hoặc nếu nó không có loại kèm theo có thể có [NullableContext]thuộc tính. Trước tiên chúng tôi tìm kiếm [Nullable], sau đó nếu chúng tôi không tìm thấy nó, chúng tôi sẽ tìm [NullableContext]loại kèm theo.

Trình biên dịch có thể nhúng các thuộc tính vào trong cụm và vì chúng ta có thể đang xem một loại từ một cụm khác, nên chúng ta cần thực hiện tải chỉ phản xạ.

[Nullable]có thể được khởi tạo với một mảng, nếu thuộc tính là chung. Trong trường hợp này, phần tử đầu tiên đại diện cho thuộc tính thực tế (và các phần tử khác đại diện cho các đối số chung). [NullableContext]luôn luôn được khởi tạo với một byte đơn.

Một giá trị 2có nghĩa là "nullable". 1có nghĩa là "không thể rỗng" và 0có nghĩa là "không biết gì".


Nó thực sự khó khăn. Tôi chỉ tìm thấy một trường hợp sử dụng không được bao gồm bởi mã này. giao diện công cộng IBusinessRelation : ICommon {}/ public interface ICommon { string? Name {get;set;} }. Nếu tôi gọi phương thức trên IBusinessRelationvới Tài sản, Nametôi nhận được sai.
gsharp

@gsharp Ah, tôi đã không thử nó với các giao diện, hoặc bất kỳ loại thừa kế nào. Tôi đoán đó là một sửa chữa tương đối dễ dàng (nhìn vào các thuộc tính ngữ cảnh từ các giao diện cơ bản): Tôi sẽ thử và sửa nó sau
canton7

1
không có vấn đề gì Tôi chỉ muốn đề cập đến nó. Thứ vô giá trị này đang khiến tôi phát điên ;-)
gsharp

1
@gsharp Nhìn vào nó, bạn cần chuyển loại giao diện xác định thuộc tính - nghĩa là ICommonkhông IBusinessRelation. Mỗi giao diện xác định riêng của nó NullableContext. Tôi đã làm rõ câu trả lời của mình và thêm kiểm tra thời gian chạy cho việc này.
cant7
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.