构建一个行为类似于Nullable <t> </t>的类

时间:2013-05-17 12:10:52

标签: c# operator-keyword

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

在以下示例中,check1&amp; check2都有效。

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

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

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

任何指针都会受到赞赏。

3 个答案:

答案 0 :(得分: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有一个excellent blog series

据我所知,编译器本身将写入的代码/ IL改为完全不同的指令集。 Eric的third entry系列剧开始证明这一点。这是因为它通常我认为它处理空值的特殊情况。

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

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

bool check3 = trackedValue >= DateTime.NowtrackedValue的情况下,请考虑您的第null行。对于Nullable<DateTime>,它有点像这样(请注意,这不是完全它是什么,请参阅Eric的系列。这仅用于传达概念):

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

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

答案 1 :(得分:1)

您已明确声明转换运算符,因此在bool check3 = trackedValue >= DateTime.Now; //Does not work时,这应该有效:

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

另一种行动方式当然是宣称它是隐含的。

答案 2 :(得分:1)

将运算符T更改为隐式:

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