【问题标题】:Read/Write a Nullable Type using BinaryReader?使用 BinaryReader 读取/写入 Nullable 类型?
【发布时间】:2013-04-09 13:33:26
【问题描述】:

我正在重载 System.IO BinaryReader 以序列化某些类以用于文件存储。我在处理字典之类的项目时没有遇到任何问题,但使用可空类型并没有成功。有可能吗?具体来说,我正在尝试十进制?和字符串?,但任何类型都应该适合我的解决方案。

出于特定的业务原因,我必须进行二进制序列化,因此请将响应限制为仅适用于此的解决方案。

例如...读取/写入字节数组我使用这些方法:

    public byte[] ReadByteArray()
    {
        int len = ReadInt32();
        if (len > 0) return ReadBytes(len);
        if (len < 0) return null;
        return new byte[0];
    }

    public override void Write(byte[] b)
    {
        int len = b.Length;
        Write(len);
        if (len > 0) base.Write(b);
    }

【问题讨论】:

  • 你能提供一个代码示例吗?
  • 有什么原因不能使用内置的 BinaryFormatter 吗?
  • 内置有速度问题。通过这种方式,我的速度提高了 20-100 倍。我想我通过检查值是否在开始时为空来解决它,然后根据需要使用默认阅读器。
  • byte[] myNull = ReadByteArray();//withnegative int32 'len'

标签: c# nullable binary-serialization


【解决方案1】:

您需要添加某种标志来让读者知道它是否应该读取下一个字节。

public decimal? ReadNullableDecimal()
{
    bool hasValue = ReadBoolean();
    if (hasValue) return ReadDecimal();
    return null;
}

public void Write(decimal? val)
{
    bool hasValue = val.HasValue;
    Write(hasValue)
    if(hasValue)
        Write(val.Value);
}

但是我们可以很聪明地创建一个适用于所有基于结构的类型的通用方法

public Nullable<T> ReadNullable<T>(Func<T> ReadDelegate) where T : struct
{
    bool hasValue = ReadBoolean();
    if (hasValue) return ReadDelegate();
    return null;
}

public void Write<T>(Nullable<T> val) where T : struct
{
    bool hasValue = val.HasValue;
    Write(hasValue)
    if(hasValue)
        Write(val.Value);
}

如果我想使用我的ReadNullable 函数来读取Int32,我会这样称呼它

Int32? nullableInt = customBinaryReader.ReadNullable(customBinaryReader.ReadInt32);

所以它会测试该值是否存在,如果存在则调用传入的函数。


编辑:在上面睡觉后,Write&lt;T&gt; 方法可能无法像您期望的那样工作。因为T 不是一个定义明确的类型,唯一可以支持它的方法是Write(object),二进制编写器不支持开箱即用。 ReadNullable&lt;T&gt; 仍然可以工作,如果你想继续使用Write&lt;T&gt;,你需要得到val.Value dynamic 的结果。您需要进行基准测试,看看是否存在任何性能问题。

public void Write<T>(Nullable<T> val) where T : struct
{
    bool hasValue = val.HasValue;
    Write(hasValue)
    if(hasValue)
        Write((dynamic)val.Value);
}

【讨论】:

  • 如果值为 null,则写入 0 代替该值。读取时,如果值为null,则只需读取零并将其丢弃。这样,您就不需要标志了。
  • 嗯@Tarik,那么您如何区分decimal? foo = 0decimal? foo = null 之间的差异?
  • 读取 hasValue 布尔值。 Case True,ReadDecimal 并返回它。案例 hasValue false,ReadDecimal 并返回 null。
  • @Tarik 我不明白那是“不需要标志”,您的第一步是“读取 hasValue boolean”。我可以看到调用 Write(default(T)) 而不是什么都不写,但这只是一个腰部空间和额外的 I/O,这是不必要的,会减慢读者的速度。
【解决方案2】:
public decimal ReadDecimal()
{
    int len = ReadInt32();
    if (len > 0) return base.ReadDecimal();
    if (len < 0) return null;
    return new decimal;
}


public override void WriteDecimal(decimal d)
{
    if (d==null)
        WriteInt32(-1);
    else
    {
        WriteInt32(sizeof(d)); //16
        Write(d);
    }
}

【讨论】:

  • 当然 WriteDecimal(decimal d) 更明确(为了代码可读性)并且与 ReadDecimal() 对称。此外,“WriteDecimal”与超类“Write”不同,因此避免了“基础”规范。
  • 为什么要在 Int32 长度上浪费四个字节,而您甚至不需要使用一个简单的单字节 bool 来确定是否应该读取一个值?如果您试图通过不写入非空值 0 来节省空间,那么您也可以为此使用单个字节:0 表示非空 0、1 表示已存储值并且可以读取,2 表示空值。或者,如果您真的喜欢用负数表示 null,请改用 sbyte...
猜你喜欢
  • 2020-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-02
  • 2012-03-16
  • 1970-01-01
  • 2020-12-31
相关资源
最近更新 更多