【问题标题】:Building a class that behaves like Nullable<T>构建一个行为类似于 Nullable<T> 的类
【发布时间】:2013-05-17 12:10:51
【问题描述】:

我正在尝试构建一个行为类似于Nullable&lt;T&gt; 的类,特别是我无需显式调用nullable.Value 即可访问Nullable&lt;T&gt; 类的基础值的方式。

在以下示例行中,check1check2 都有效。

Nullable<DateTime> nullable = new DateTime();
bool check1 = nullable >= DateTime.Now; //Works
bool check2 = nullable.Value >= DateTime.Now; //Works

我构建了自己的类TrackedValue,它会记住它包装的值是否已更改。我基于Nullable&lt;T&gt; 构建了隐式和显式运算符。

Nullable&lt;T&gt;定义

public struct Nullable<T> where T : struct
{
    public Nullable(T value);

    public static explicit operator T(T? value);
    public static implicit operator T?(T value);

    ...
}

TrackedValue&lt;T&gt;定义

public class TrackedValue<T> : IChangeTracking
{
    ...

    T trackedValue;
    public T Value
    {
        get
        {
            return this.trackedValue;
        }
        set
        {
            this.trackedValue = value;
        }
    }

    public static explicit operator T(TrackedValue<T> value)
    {
        return value.Value;
    }

    public static implicit operator TrackedValue<T>(T value)
    {
        return new TrackedValue<T>() { Value = value };
    }
}

所以我期待以下工作,但是 check3 将无法编译,因为:

Argument 1: cannot convert from 'TrackedValue&lt;System.DateTime&gt;' to 'System.DateTime'

TrackedValue<DateTime> trackedValue = new DateTime();
bool check3 = trackedValue >= DateTime.Now; //Does not work
bool check4 = trackedValue.Value >= DateTime.Now; //Works

任何指针将不胜感激。

【问题讨论】:

    标签: c# operator-keyword


    【解决方案1】:

    该行特别不起作用,因为它需要implicit 转换,但您将其标记为explicit

    public class TrackedValue<T> : IChangeTracking
    {
        T trackedValue;
        public T Value
        {
            get
            {
                return this.trackedValue;
            }
            set
            {
                this.trackedValue = value;
            }
        }
    
        public static implicit operator T(TrackedValue<T> value)
        {
            return value.Value;
        }
    
        public static implicit operator TrackedValue<T>(T value)
        {
            return new TrackedValue<T>() { Value = value };
        }
    }
    

    但很自然,您想模仿Nullable&lt;T&gt; 模型。那么为什么Nullable&lt;T&gt; &lt;= T 隐含地工作而不是你的呢?我相信它来自 C# 编译器本身。 Eric Lippert 有一个excellent blog series 关于如何编译/优化 Nullables。

    据我了解,编译器本身会将编写的代码/IL 完全更改为不同的指令集。 Eric 在该系列中的third entry 开始证明这一点。这是因为我相信它通常处理空值的特殊情况。

    我不确定您是否可以解决此问题,但也许最简单的方法是将那里的一个转换运算符简单地标记为implicit,并希望它不会对您造成任何关于一致性的重大问题在TrackedValue&lt;T&gt;Nullable&lt;T&gt; 之间。

    编辑:其中一个不一致的项目将说明如何进行比较。

    trackedValuenull 的情况下考虑您的行bool check3 = trackedValue &gt;= DateTime.Now。对于Nullable&lt;DateTime&gt;,它有点像这样(请注意,这不是确切它是什么,请参阅 Eric 的系列。这只是为了传达这个概念):

    check3 = trackedValue.HasValue ? trackedValue.Value >= DateTime.Now : false;
    

    编译器避免甚至调用转换运算符。另一方面,您会尝试运行您的隐式转换(假设您将其切换为隐式),这可能会导致 NullReferenceException 不受欢迎(隐式运算符应该抛出异常)。 Nullable&lt;T&gt; 将转换运算符定义为 explicit 的原因是,在那些你直接进行强制转换的时候(例如,DateTime casted = (DateTime)myNullableDateTime;),如果值为 null,则可能会引发异常。

    【讨论】:

    • 感谢您的信息,我很想不必输入.Value,因此更改操作符很好。对于类型之间的一致性,我实际上并没有那么大惊小怪。感谢大家的帮助。
    • Re: NullReferenceException,我现在可以忍受,代码不适合一般消费。另外trackedValue.Value = null 是一个有效的情况是我的情况。
    • @JamesWood 不客气!我用一个具体的差异示例编辑了我的帖子。希望它对你有用!
    【解决方案2】:

    您明确声明了转换运算符,因此在 bool check3 = trackedValue &gt;= DateTime.Now; //Does not work 时,这应该可以工作:

    bool check3 = (DateTime)trackedValue >= DateTime.Now;
    

    当然,另一种做法是将其声明为隐式。

    【讨论】:

      【解决方案3】:

      将运算符T 更改为隐式:

      public static implicit operator T(TrackedValue<T> value)
      {
          return value.Value;
      }
      

      【讨论】:

        猜你喜欢
        • 2013-03-26
        • 2015-08-12
        • 2012-12-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多