给出以下课程
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 在这两种情况下都是“字符串”。
有什么建议
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;
}
}
请注意,每次都以这种方式调用反射可能成本高昂(甚至在某些情况下不可用),因此您应该考虑以下选项: