如何在使用属性选择器时检查可为空

问题描述 投票:0回答:1

给出以下课程

   public class Anonymizer : IAnonymizer
{
    public void Anonymize<TEntity, TProperty>(TEntity entity, Expression<Func<TEntity, TProperty>> propertySelector)
    {
        var value = typeof(TProperty) switch
        {
            { } t when IsNullable(t)  => (TProperty?)(object) null!,
            { } t when t == typeof(string) => (TProperty) (object) "<Anonymized>",
            { } t when t == typeof(int) => (TProperty) (object) -1,
            _ => default,
        };
        
        var memberExpression = (MemberExpression)propertySelector.Body;
        var property = (PropertyInfo)memberExpression.Member;
        property.SetValue(entity, value);
    }

    private static bool IsNullable(Type t)
    {
        return !t.IsValueType ||  Nullable.GetUnderlyingType(t) != null;
    }
}

以及以下测试

  [Fact]
    public void Test_string()
    {
        var sut = new Anonymizer();
        
        var myObject = new MyClass {MyText = "Hello World", MyNumber = 42};

        sut.Anonymize(myObject, e => e.MyText);

        myObject.MyText.Should().Be("<Anonymized>");
        myObject.MyNumber.Should().Be(42);
    }

    
    [Fact]
    public void Test_int()
    {
        var sut = new Anonymizer();
        
        var myObject = new MyClass {MyText = "Hello World", MyNumber = 42};

        sut.Anonymize(myObject, e => e.MyNumber);

        myObject.MyText.Should().Be("Hello World");
        myObject.MyNumber.Should().Be(-1);
    }
    
        
    [Fact]
    public void Test_NullableString()
    {
        var sut = new Anonymizer();
        
        var myObject = new MyClass
        {
            MyText = "Hello World", 
            MyNumber = 42,
            MyNullableString = "Hello World"
        };

        sut.Anonymize(myObject, e => e.MyNullableString);
        
        myObject.MyNumber.Should().Be(42);
        myObject.MyNullableString.Should().BeNull();
    }

    class MyClass
    {
        public string MyText { get; set; }
        public int MyNumber { get; set; }
        public string? MyNullableString { get; set; }
    }

似乎没有办法区分“string”类型的属性和“string?”类型的属性。看起来 TProperty 在这两种情况下都是“字符串”。

有什么建议

c# reflection nullable nullable-reference-types
1个回答
2
投票

您需要分析

NullabilityInfoContext
,大致如下:

public class Anonymizer : IAnonymizer
{
    public void Anonymize<TEntity, TProperty>(TEntity entity, Expression<Func<TEntity, TProperty>> propertySelector)
    {
        var value = typeof(TProperty) switch
        {
            { } t when IsNullableValueType(t)  => (TProperty?)(object) null!,
            { } t when IsNullableReferenceType(propertySelector)  => (TProperty?)(object) null!,
            // ...
        };
        
        // ...
    }

    private static bool IsNullableValueType(Type t) => Nullable.GetUnderlyingType(t) != null;
    
    private static bool IsNullableReferenceType<TEntity, TProperty>(Expression<Func<TEntity, TProperty>> propertySelector)
    {
        if (typeof(TProperty).IsValueType) return false;
        var memberExpression = (MemberExpression)propertySelector.Body;
        var property = (PropertyInfo)memberExpression.Member;

        NullabilityInfoContext context = new();
        var nullabilityInfo = context.Create(property); 
        return nullabilityInfo.ReadState == NullabilityState.Nullable;
    }
}

请注意,每次都以这种方式调用反射可能成本高昂(甚至在某些情况下不可用),因此您应该考虑以下选项:

  • 使用源生成器(如果没有更好地概述代码库和用例,很难判断它是否可行)
  • 缓存反射(请参阅以下答案之一:onetwo Three以获得一些想法)
© www.soinside.com 2019 - 2024. All rights reserved.