定义精确大小的内存结构

时间:2016-07-12 14:31:56

标签: c# data-structures struct marshalling shared-memory

我正在做一些内存共享,并且具有以下结构,我将在内存共享区域中使用...

[StructLayout(LayoutKind.Sequential)]
public struct MySharedMemory
{
    //Bools
    public bool Flag1;
    public bool Flag2;
    public bool Flag3;

    //DateTimes
    public DateTime LastWrite;
    public DateTime LastRead;

    //Longs
    public long SrcSize;

    //Strings that are a max of 250 characters
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 250)]
    public string SrcFile;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 250)]
    public string DestFile;

    //Ints
    public int Count;

    //An array of strings that are a max of 100 characters
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100, ArraySubType = UnmanagedType.Struct)]
    public FileInfo[] FilesToUpdate;
}

我知道上面的所有定义都是正确的,但DataTime除外。我刚刚添加了这些,我不确定这些是固定大小还是我需要定义一些特殊的东西,就像我为字符串做的那样。我的问题是,除了数组和字符串之外,还有任何类型没有固定大小(特别是我的DateTime定义可以)吗?

2 个答案:

答案 0 :(得分:1)

不幸的是DateTime对于Marshaling来说是weird type(至少,它有一个非常出乎意料的令人惊讶的行为。)

首先,因为它只有一个Auto字段,因此它long结构(并且这使得它不具有blittable并且具有案例的所有consequences )。

你可能会认为包装在一个blittable结构中(这里没有什么新东西,它是常用方式来编组非blittable类型):

public struct BlittableDateTime
{
    private BlittableDateTime(long ticks)
    {
        _ticks = ticks;
    }

    public static implicit operator BlittableDateTime(DateTime value)
    {
        return new BlittableDateTime(value.Ticks);
    }

    public static implicit operator DateTime(BlittableDateTime value)
    {
        return new DateTime(value._ticks);
    }

    private readonly long _ticks;
}

到目前为止,你可能会想到这么好。但是,我们将<{1}}(从1/1/0001的100 ns刻度数)转换为8字节整数值,而 unmanaged em>世界。在非托管世界中,您可能拥有:DateTimetime_tFILETIMESYSTEMTIME和(许多)其他人,但none of them exactly matches granularity and range。 NET DATE。更多恼人的它实际上并不是原始 DateTime值,因为某些位具有特殊含义,源代码来源:

  

位63-64:描述日期时间的DateTimeKind值的四态值...

您需要转化,在本例中我选择long long

FILETIME

编辑:如何使用它?我们定义了两个隐式运算符,然后转换到/ public static implicit operator BlittableDateTime(DateTime value) { return new BlittableDateTime(value.ToFileTime()); } public static implicit operator DateTime(BlittableDateTime value) { return DateTime.FromFileTime(value._ticks); } 的转换是自动,您不需要直接管理托管代码中的DateTime结构(还要注意构造函数是私有的,所有转换是通过定义的运算符):

FILETIME

但是我们没有为此类型定义任何比较运算符。如果你不经常做,你有两个选择,第一个是铸造:

BlittableDateTime time1 = DateTime.UtcNow;
DateTime time2 = time1;

或者,您可以添加if ((DateTime)time1 == time2) { // Do something... } 属性,该属性返回Value(模仿DateTime用法):

Nullable<T>

像这样使用:

public DateTime Value
{
    get { return (DateTime)this; }
}

关于转化的另一个注意事项。请注意,并非每次转换都是可能的 - 在这种情况下 - if (time1.Value == time2) { // Do something... } 具有不同的范围。 FILETIME 在1/1/1601开始并且以100 ns的粒度跨越+/- 30,000年(或多或少),因为它可能是负数。 FILETIME从1/1/0001开始,它有效地使用62位信息(2 62 ticks),但最大值是9999年12月31日。 另一个问题:从DateTime转换回来时,current implementation不支持负值,有效可用范围介于1601年1月1日(最小 FILETIME)和12月31日之间9999(最大FILETIMEDateTime值。)

在处理日期时,请不要忘记他们(几乎)始终与日历相关联,而某些日历可能会有不同的限制:例如,台历从1/1/0001开始(即1 / 1/1912年公历)和Um Al Qura日历结束于12/29/1450(格里高利历5/13/2029)。

答案 1 :(得分:0)

我会使用long,只使用属性将其公开为DateTime

public struct YourStruct
{
...
    private long myTimeAsTicks;
    public DateTime MyTime 
    {
        get { return new DateTime(myTimeAsTicks); }
        set { myTimeAsTicks = value.Ticks; }
    }
...
}

获取结构的实际大小实际上有点棘手,请参阅https://stackoverflow.com/a/3362736/870604