2

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

在下面的示例行中check1&check2都有效。

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

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

Nullable<T>定义

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<T>定义

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<System.DateTime>' to 'System.DateTime'

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

任何指针将不胜感激。

4

3 回答 3

2

该行特别不起作用,因为它需要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<T>模型。那么为什么Nullable<T> <= T隐含地工作而不是你的呢?我相信它来自 C# 编译器本身。Eric Lippert 有一个关于如何编译/优化 Nullables的优秀博客系列。

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

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

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

bool check3 = trackedValue >= DateTime.Now在这种情况下考虑您的trackedValue线路null。对于一个Nullable<DateTime>它有点像这样(请注意,这并不完全是它的样子,请参阅 Eric 的系列。这只是为了传达这个概念):

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

编译器甚至避免调用转换运算符。另一方面,您的会尝试运行您的隐式转换(假设您将其切换为隐式),这可能会导致NullReferenceException不赞成(隐式运算符不应抛出异常)。Nullable<T>将转换运算符定义为的原因是,如果值为空explicit,则直接强制转换(例如, )可能会引发异常。DateTime casted = (DateTime)myNullableDateTime;

于 2013-05-17T12:29:10.453 回答
1

您明确声明了转换运算符,因此 whilebool check3 = trackedValue >= DateTime.Now; //Does not work应该可以工作:

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

当然,另一种做法是声明它是隐含的。

于 2013-05-17T12:28:58.530 回答
1

将运算符更改T为隐式:

public static implicit operator T(TrackedValue<T> value)
{
    return value.Value;
}
于 2013-05-17T12:29:22.550 回答