c# - 如何设计表示既不是空也不是空格的字符串的类型
问题描述
很多时候,我们在业务实体的构造函数中传递字符串,我们希望确保这些字符串实际上带有一个值。在这种情况下,我们对构造函数参数进行验证,并且ArgumentException
每次传入的字符串为空或空格时都会抛出一个。
这是我的意思的一个例子:
public class Person
{
public string Name { get; }
public Person(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("A person name cannot be null or white space", nameof(name));
}
this.Name = name;
}
}
厌倦了重复自己,我决定设计一种自动安全类型,表示既不是空也不是空格的字符串。这样我可以直接在我的业务代码中使用该类型的实例并避免任何验证,因为该类型的每个实例都是自动安全的(换句话说,验证代码现在在一个地方,即类型本身的代码)。
这是NonEmptyString
结构(原始代码在这里):
using System;
namespace Deltatre.Utils.Types
{
/// <summary>
/// This type wraps a string which is guaranteed to be neither null nor white space
/// </summary>
public struct NonEmptyString
{
/// <summary>
/// Implicit conversion from <see cref="NonEmptyString"/> to <see cref="string"/>
/// </summary>
/// <param name="nonEmptyString">The instance of <see cref="NonEmptyString"/> to be converted</param>
public static implicit operator string(NonEmptyString nonEmptyString)
{
return nonEmptyString.Value;
}
/// <summary>
/// Explicit conversion from <see cref="string"/> to <see cref="NonEmptyString"/>
/// </summary>
/// <param name="value">The instance of <see cref="string"/> to be converted</param>
/// <exception cref="InvalidCastException">Throws <see cref="InvalidCastException"/> when <paramref name="value"/> is null or white space</exception>
public static explicit operator NonEmptyString(string value)
{
try
{
return new NonEmptyString(value);
}
catch (ArgumentException ex)
{
throw new InvalidCastException($"Unable to convert the provided string to {typeof(NonEmptyString).Name}", ex);
}
}
/// <summary>
/// Creates new instance of <see cref="NonEmptyString"/>
/// </summary>
/// <param name="value">The string to be wrapped</param>
/// <exception cref="ArgumentException">Throws <see cref="ArgumentException"/> when parameter <paramref name="value"/> is null or white space</exception>
public NonEmptyString(string value)
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException($"Parameter {nameof(value)} cannot be null or white space", nameof(value));
this.Value = value;
}
/// <summary>
/// Gets the wrapped string
/// </summary>
public string Value { get; }
/// <summary>Indicates whether this instance and a specified object are equal.</summary>
/// <param name="obj">The object to compare with the current instance. </param>
/// <returns>
/// <see langword="true" /> if <paramref name="obj" /> and this instance are the same type and represent the same value; otherwise, <see langword="false" />. </returns>
public override bool Equals(object obj)
{
if (!(obj is NonEmptyString))
{
return false;
}
var other = (NonEmptyString)obj;
return this.Value == other.Value;
}
/// <summary>Returns the hash code for this instance.</summary>
/// <returns>A 32-bit signed integer that is the hash code for this instance.</returns>
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = (hash * 23) + (this.Value == null ? 0 : this.Value.GetHashCode());
return hash;
}
}
/// <summary>
/// Compares two instances of <see cref="NonEmptyString"/> for equality
/// </summary>
/// <param name="left">An instance of <see cref="NonEmptyString"/></param>
/// <param name="right">An instance of <see cref="NonEmptyString"/></param>
/// <returns></returns>
public static bool operator ==(NonEmptyString left, NonEmptyString right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two instances of <see cref="NonEmptyString"/> for inequality
/// </summary>
/// <param name="left">An instance of <see cref="NonEmptyString"/></param>
/// <param name="right">An instance of <see cref="NonEmptyString"/></param>
/// <returns></returns>
public static bool operator !=(NonEmptyString left, NonEmptyString right)
{
return !(left == right);
}
}
}
使用我的新类型,我可以通过这种方式更改以前的代码:
public class Person
{
public NonEmptyString Name { get; }
public Person(NonEmptyString name)
{
this.Name = name;
}
}
这种设计的唯一问题是默认构造函数表示,它始终可用,因为我的类型是struct
.
如果有人使用我的代码编写var myString = new NonEmptyString();
,他会得到一个封装null
引用的类型的实例:这是我想避免的,因为这样做会使我的自动安全类型的整个目的无效。换句话说,我不想依赖程序员不调用默认构造函数,我想让它不可能误用这种类型。
我想出了几个想法:
为只读属性提供默认值
Value
,例如“NA”。这样,即使调用默认构造函数,获得的实例也封装了一个非 null 和非空白值。添加一个标志,指示类型是否已初始化,默认值为
false
. 此状态是只读的,并且true
仅在接收字符串参数的构造函数重载中更改为。这样,可以将保护添加到类型的任何成员,以便InvalidOperationException
程序员每次尝试使用该类型的未初始化实例(即通过调用默认构造函数获得的类型的实例)时都可以引发.
你有什么建议吗?你更喜欢什么方法?
解决方案
您可以使用支持字段_value
并为属性实现 getter,Value
而不是使用自动属性。喜欢
public string Value => _value ?? "";
然后让每个函数在_value
为空时工作;
推荐阅读
- javascript - 返回包含条件的数组的长度
- python - Django 仅在同一日期有多个公司交易的情况下查询交易
- java - SnakeYaml“找不到属性错误”
- javascript - Web 推送通知(使用服务工作者)
- javascript - 使用 fetch API 通过 formData 传递表单上输入的所有值的问题
- r - 在excel中保留几列中的匹配行
- sql-server - SQL Server 同时对表进行变更跟踪和变更数据捕获
- python - 我可以将 DateTimeFields 与标签“if”进行比较吗?
- terminal - “/bin”不在 PATH 中
- javascript - 管理模板,避免在重新加载时打开侧边栏