如何通过处理default(T)来正确避免原始痴迷?

时间:2018-09-21 22:11:50

标签: c# .net struct default

我想避免对以下struct的痴迷。我这样做有两个目标:

  • 使方法签名更诚实
  • 确保无效值不存在

实施:

public struct SomeId : IEquatable<SomeId>
{
    public static readonly SomeId Empty = new SomeId(String.Empty);

    private SomeId(string someId)
    {
        Value = someId;
    }

    public string Value { get; }

    public static bool operator ==(SomeId left, SomeId right) => left.Equals(right);
    public static bool operator !=(SomeId left, SomeId right) => !(left == right);
    public static implicit operator string(SomeId id) => id.Value;
    public override int GetHashCode() => Value.GetHashCode();
    public override bool Equals(object obj) => Value.Equals(obj);
    public bool Equals(SomeId other) => String.Equals(Value, other.Value, StringComparison.Ordinal);

    public static SomeId? Create(string value)
    {
        if (String.IsNullOrWhiteSpace(value))
        {
            return new SomeId(value);
        }

        return null;
    }
}

很多代码,但仍然完美!确实,它解决了主要问题,即我不会到处传递字符串,而是给方法赋予有意义的签名。

仍然,仅通过default(SomeId)创建新实例就可以将无效值潜入我的应用程序中-由于Value的类型为string,我将得到{{1} }的状态为SomeId

什么是最好的解决方案?我什至应该在乎吗?

当然可以这样做:

Value = null

...但是,每当我访问此属性时,该附加的null检查都会困扰我。

1 个答案:

答案 0 :(得分:1)

你应该在乎吗?是的,我想是这样。创建零初始化的结构(default(SomeId)new SomeId()new SomeId[n])非常容易,并且该结构在该状态下在语义上是无效的。

您有几种选择:

  • 吸气剂中的空合并(您建议的解决方案)。您是对的,如果该字段为空,则将始终导致执行更多指令。问题是,这些额外的指令(例如,加载null,比较是否相等,加载静态字段)是否会对执行速度产生可衡量的影响?
  • 在吸气剂中检查null,并根据需要将字段设置为string.Empty。从技术上讲,这是一种带有副作用的吸气剂(即使数据是封装的),有些人对此有强烈的意见,但是您也可以将其称为延迟初始化。
  • 将默认实例声明为无效,例如ImmutableArray<T>