2016-07-12 9 views
3

ich einige gemeinsame Nutzung von Speicher zu tun und haben die folgende Struktur, die ich in dem Speicher-Sharing-Bereich werden mit ...definieren präzise Größe Speicherstrukturen

[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; 
} 

Ich weiß, dass alle obigen Definitionen richtig sind, mit Ausnahme von DataTime. Ich habe sie einfach hinzugefügt und bin mir nicht sicher, ob das eine feste Größe ist oder ich etwas Spezielles definieren muss, wie ich es für die Saiten getan habe. Meine Frage ist, abgesehen von Arrays und Strings gibt es einen Typ, der keine feste Größe hat (speziell ist meine DateTime Definitionen ok)?

Antwort

1

Leider DateTime ist ein weird type für Marshalling (oder zumindest hat es ein ziemlich unerwartetes überraschendes Verhalten.)

Vor allem, weil es einistStruktur (und dies macht es nicht-blitable und mit allen consequences des Gehäuses) mit nur einem einzigen long Feld.

Sie mögen denken, zu Wrap es in einem blitfähig struct (hier nichts Neues, es ist der gemeinsame Weg nicht blitfähige Typs Marschall):

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; 
} 

So weit so gut, Sie vielleicht denken, . Allerdings sind wir Umwandlung eine DateTime (Anzahl von 100 ns Ticks von 1/1/0001) zu einem 8-Byte-Integer-Wert ohne jeden äquivalenten Typ in unmanaged Welt. In nicht verwalteten Welt können Sie haben: time_t, FILETIME, SYSTEMTIME, DATE und (viele) andere, aber none of them exactly matches granularity and range von .NET DateTime. Noch mehr ärgerlich es ist nicht wirklich ein rohlong long Wert, da einige Bits eine besondere Bedeutung haben, aus dem Quellcode:

Bits 63-64: Ein Vier-Zustands-Wert, der das Datetimekind Wert des Datums beschreibt Zeit ...

Sie benötigen einen Umwandlung, in diesem Beispiel gehe ich mit 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); 
} 

Bearbeiten: wie man es benutzt? Wir definierten zwei implizite Operatoren dann Conversions zu/von DateTimeautomatische sind, brauchen Sie nicht direkt FILETIME Struktur in verwaltetem Code verwalten (beachten Sie auch, dass Konstruktor privat ist, alle Konvertierungen durch definierten Operatoren gehen werden):

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

Allerdings haben wir für diesen Typ keine Vergleichsoperatoren definiert.Wenn Sie das nicht tun es oft zwei Alternativen, die Sie haben, erste ist Gießen:

if ((DateTime)time1 == time2) { 
    // Do something... 
} 

Alternativ können Sie Value Eigenschaft hinzufügen, die DateTime zurückgibt (zu Nullable<T> Nutzung zu imitieren):

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

Gebraucht wie folgt aus:

if (time1.Value == time2) { 
    // Do something... 
} 

Noch eine n Hinweise zu Conversions Beachten Sie, dass nicht jede Konvertierung möglich ist und - in diesem Fall - FILETIME einen anderen Bereich hat. FILETIMEbeginnt am 1/1/1601 und mit einer Granularität von 100 ns erstreckt es sich +/- 30.000 Jahre (mehr oder weniger), weil es negativ sein kann. DateTime beginnt am 1.1.0001 und verwendet effektiv 62 Bits von Informationen (2 Ticks) aber Maximalwert ist der 31. Dezember 9999. Ein weiteres Problem : current implementation nicht negative Werte unterstützen, wenn von der Rückseite FILETIME dann effektiv nutzbare Bereich Umwandeln ist zwischen dem 1. Januar 1601 (mindestens positivFILETIME) und 31.12.9999 (maximal DateTime und DATE Wert).

Bei der Arbeit mit Daten nicht vergessen, dass sie (fast) immer mit einem Kalender verbunden sind und einige Kalender können unterschiedliche Grenzen haben: zum Beispiel beginnt Taiwan Kalender um 1/1/0001 (das ist 1/1/1912 im Gregorianischen Kalender) und Um Al Qura Kalender endet am 29.12.1450 (5/13/2029 im Gregorianischen Kalender).

+0

Im Grunde genommen erstelle ich eine neue Struktur mit den beiden obigen Beispielen kombiniert (mit dem zweiten, um den Inhalt des ersten zu überschreiben). Dann definiere ich 'LastWrite' und' LastRead' als 'BlittableDateTime' korrekt? Als Beispiel kann 'public BlittableDateTime LastWrite;'. Dann muss ich nichts für 'BlittableDateTime' Typen marschieren oder? –

+0

Genau. Ändere FILETIME zu dem, was am besten zu deiner nicht verwalteten Verwendung passt –

+0

Das sollte gut funktionieren, danke! Ein Problem, auf das ich stoße ... Wenn ich versuche, etwas wie "BlittableDateTime test = BlittableDateTime (DateTime.Now.ToFileTime)" zu machen; wird es nicht funktionieren. Können Sie Ihre Antwort vielleicht aktualisieren, um etwas außerhalb der Struktur zu verwenden? Vielleicht nach dem Definieren der Struktur, erstellen Sie ein Objekt des Strukturtyps und vergleichen Sie es dann mit einem anderen Objekt des gleichen Typs? Beispiel: 'BlittableDateTime test = BlittableDateTime (DateTime.Now.ToFileTime);' dann 'BlittableDateTime test2 = BlitableDateTime (DateTime.Now.ToFileTime);' dann 'if (test! = Test2) {// tue arbeiten}' –

0

würde ich ein long zu verwenden, und es nur aussetzen als DateTime eine Eigenschaft mit:

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

Es ist eigentlich ein bisschen schwierig, um die tatsächliche Größe einer Struktur zu erhalten, siehe https://stackoverflow.com/a/3362736/870604

+0

So ändert sich die Größe von DateTime? Es ist nicht konstant wie ein int oder lang? –

Verwandte Themen