我正在做一些内存共享,并且具有以下结构,我将在内存共享区域中使用...
[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定义可以)吗?
答案 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>世界。在非托管世界中,您可能拥有:DateTime
,time_t
,FILETIME
,SYSTEMTIME
和(许多)其他人,但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(最大FILETIME
和DateTime
值。)
在处理日期时,请不要忘记他们(几乎)始终与日历相关联,而某些日历可能会有不同的限制:例如,台历从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