构建一个行为类似于Nullable<;T>;

本文关键字:Nullable gt lt 类似于 一个 构建 | 更新日期: 2023-09-27 18:13:59

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

在以下示例中,行check1&CCD_ 5都起作用。

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

我构建了自己的类TrackedValue,它可以记住它包装的值是否已更改。我在CCD_ 7的基础上构建了隐式&显式运算符。

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

任何建议都将不胜感激。

构建一个行为类似于Nullable<;T>;

这一行特别不起作用,因为它需要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完全更改为不同的指令集。孙在这个系列的第三个作品开始展示这一点。这是因为我相信它通常会处理null的特殊情况。

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

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

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

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

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

您显式地声明了转换运算符,因此当bool check3 = trackedValue >= DateTime.Now; //Does not work时,这应该起作用:

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

当然,另一种做法是宣布它是隐含的。

将运算符T更改为隐式:

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