正在验证c#记录的属性

  • 本文关键字:属性 记录 验证 c#
  • 更新时间 :
  • 英文 :


记录是C#中的一种新对象类型。当需要不可变对象时,它可以节省大量的类型。在某些情况下,可能需要验证记录的属性。在这个问题上,提出了一种模式。这个解决方案是有效的,但我认为我找到了一个更优雅的方法(见我的回复(。我建议:

record Person(string FirstName, string LastName, int Age, Guid Id)
{
private bool _dummy = 
Check.StringArg(FirstName) && Check.StringArg(LastName) && Check.IntArg(Age);
internal static class Check
{
static internal bool StringArg(string s)
{
if (s == "" || s == null)
throw new ArgumentException("Argument cannot be null or empty");
else return true;
}
static internal bool IntArg(int a)
{
if (a < 0)
throw new ArgumentException("Argument cannot be negative");
else return true;
}
}
}

这个想法是使用编译器生成的Properties,并让一些验证函数在出现错误时抛出异常。不幸的是,有一个毫无意义的_dummy变量。有办法摆脱它吗?

这是一个很好的想法,它启发我围绕这个想法实现一种基于扩展方法的验证框架。我创建了以下验证类(为了简洁起见,删除了其他验证(:

public static class Validation
{
public static bool IsValid<T>(this T _)
{
return true;
}
public static T NotNull<T>(T @value, [CallerArgumentExpression("value")] string? thisExpression = default)
{
if (value == null) throw new ArgumentNullException(thisExpression);
return value;
}
public static string LengthBetween(this string @value, int min, int max, [CallerArgumentExpression("value")] string? thisExpression = default)
{
if (value.Length < min) throw new ArgumentOutOfRangeException(thisExpression, $"Can't have less than {min} items");
if (value.Length > max) throw new ArgumentOutOfRangeException(thisExpression, $"Can't have more than {max} items");
return value;
}
public static IComparable<T> RangeWithin<T>(this IComparable<T> @value, T min, T max, [CallerArgumentExpression("value")] string? thisExpression = default)
{
if (value.CompareTo(min) < 0) throw new ArgumentOutOfRangeException(thisExpression, $"Can't have less than {min} items");
if (value.CompareTo(max) > 0) throw new ArgumentOutOfRangeException(thisExpression, $"Can't have more than {max} items");
return value;
}
}

然后您可以将其与以下内容一起使用:

// FirstName may not be null and must be between 1 and 5
// LastName may be null, but when it is defined it must be between 3 and 10
// Age must be positive and below 200
record Person(string FirstName, string? LastName, int Age, Guid Id)
{
private readonly bool _valid = Validation.NotNull(FirstName).LengthBetween(1, 5).IsValid() &&
(LastName?.LengthBetween(2, 10).IsValid() ?? true) &&
Age.RangeWithin(0, 200).IsValid();

}

??true非常重要,它是为了确保在可为null的LastName确实为null的情况下继续验证,否则它会短路。也许使用另一个静态AllowNull方法来包装该变量的整个验证会更好(更安全(,如下所示:

public static class Validation
{
public static bool IsValid<T>(this T _)
{
return true;
}
public static bool AllowNull<T>(T? _)
{
return true;
}
public static T NotNull<T>(T @value, [CallerArgumentExpression("value")] string? thisExpression = default)
{
if (value == null) throw new ArgumentNullException(thisExpression);
return value;
}
public static string LengthBetween(this string @value, int min, int max, [CallerArgumentExpression("value")] string? thisExpression = default)
{
if (value.Length < min) throw new ArgumentOutOfRangeException(thisExpression, $"Can't have less than {min} items");
if (value.Length > max) throw new ArgumentOutOfRangeException(thisExpression, $"Can't have more than {max} items");
return value;
}
public static IComparable<T> RangeWithin<T>(this IComparable<T> @value, T min, T max, [CallerArgumentExpression("value")] string? thisExpression = default)
{
if (value.CompareTo(min) < 0) throw new ArgumentOutOfRangeException(thisExpression, $"Can't have less than {min} items");
if (value.CompareTo(max) > 0) throw new ArgumentOutOfRangeException(thisExpression, $"Can't have more than {max} items");
return value;
}
}
record Person(string FirstName, string? LastName, int Age, Guid Id)
{
private readonly bool _valid = Validation.NotNull(FirstName).LengthBetween(1, 5).IsValid() &&
Validation.AllowNull(LastName?.LengthBetween(2, 10)) &&
Age.RangeWithin(0, 200).IsValid();
}

我还是不太喜欢那个部分,但除此之外,我觉得它很酷!但还没有真正测试过:(所以要小心!

最新更新