Class DType
- Namespace
- NumSharp
- Assembly
- NumSharp.dll
NumSharp's data-type descriptor — the INSTANCE of a DTypeMeta class, standing in for NumPy's
numpy.dtype object (np.dtype('f8'), np.dtype('M8[ns]')), and the single dtype
spelling every dtype-taking API in NumSharp accepts.
public sealed class DType : IEquatable<DType>
- Inheritance
-
DType
- Implements
- Inherited Members
Remarks
Two levels (NEP 41/42). The CLASS (Meta — np.dtypes.Float64DType) owns the
behaviour: promotion, casting, the default instance; the INSTANCE owns the parameters: byteorder
and, for the parametric datetime pair, the unit (DatetimeMetadata). The 15 storage-backed
builtins have one canonical instance each (np.dtype("i8") is the same object every time —
isbuiltin == 1); a datetime descriptor or a non-native-byte-order one is a fresh object
(isbuiltin == 0), exactly as in NumPy.
Why it exists. NumPy funnels every dtype= argument through one coercion point,
numpy.dtype(...), so a Python type (float), a NumPy scalar type (np.float32),
a np.dtype instance and a dtype string ('float32', 'f4', '<f8')
are ALL valid there. NumSharp historically had three separate spellings — a C# Type,
an NPTypeCode enum, and this descriptor from dtype(string) — which forced
two-or-three overloads per function. DType collapses them: it is the one type every
dtype-taking overload accepts, and each of the other spellings converts to it IMPLICITLY, so a caller
writes whichever is convenient and it binds the single overload — mirroring NumPy's one dtype=.
The four spellings, all implicit. The following are equivalent and all bind the one
DType overload (e.g. np.sqrt(x, dtype: …)):
<ul><li><b><xref href="System.Type" data-throw-if-not-resolved="false"></xref></b> — <code>typeof(float)</code> (NumPy's Python/NumPy scalar type).</li><li><b><xref href="NumSharp.NPTypeCode" data-throw-if-not-resolved="false"></xref></b> — <code>NPTypeCode.Single</code> (NumSharp's compact storage enum; no NumPy counterpart).</li><li><b>NumPy dtype string</b> — <code>"float32"</code> / <code>"f4"</code> / <code>"<f8"</code> / <code>"M8[ns]"</code> (NumPy's <code>dtype='float32'</code> — see the casing rules below).</li><li><b><xref href="NumSharp.DType" data-throw-if-not-resolved="false"></xref> itself</b> — <code>DType.Single</code>, <code>np.dtype("f4")</code>, <code>DType.From(...)</code>.</li></ul>
A <xref href="NumSharp.DType" data-throw-if-not-resolved="false"></xref> also converts back to <xref href="System.Type" data-throw-if-not-resolved="false"></xref> and <xref href="NumSharp.NPTypeCode" data-throw-if-not-resolved="false"></xref>
implicitly, so it drops straight into code expecting either — as long as its class has storage: a
datetime descriptor has none yet (Stage A), and converting one raises <xref href="System.NotSupportedException" data-throw-if-not-resolved="false"></xref>
rather than silently degrading to the "infer" state.
None / infer is null. A null DType is the
"none/infer" state — the analog of NumPy's dtype=None. This is precisely why DType
is a class, not a struct: a nullable DType dtype = null parameter is a drop-in replacement
for the old Type dtype = null parameter, so the engine's existing null idioms keep working
verbatim — dtype?.GetTypeCode() yields NPTypeCode? (null when none) and dtype == null
tests the none state. Converting a null/Empty spelling yields
a null DType (never a throwing conversion); the explicit
DType(Type) / DType(NPTypeCode) constructors, by contrast, reject
null/Empty (use a null DType for none).
NumPy string casing (source of truth: NumPy 2.4.2). Strings are parsed by dtype(string) with NumPy's exact, case-sensitive grammar — the single-character codes differ by case:
<table><thead><tr><th class="term">code</th><th class="description">type</th></tr></thead><tbody><tr><td class="term">?</td><td class="description">bool</td></tr><tr><td class="term">b / B</td><td class="description">int8 / uint8</td></tr><tr><td class="term">h / H</td><td class="description">int16 / uint16</td></tr><tr><td class="term">i / I</td><td class="description">int32 / uint32</td></tr><tr><td class="term">q / Q</td><td class="description">int64 / uint64</td></tr><tr><td class="term">e / f / d</td><td class="description">float16 / float32 / float64</td></tr><tr><td class="term">D</td><td class="description">complex128</td></tr><tr><td class="term">M8[unit] / m8[unit]</td><td class="description">datetime64 / timedelta64 (descriptor-level in Stage A)</td></tr></tbody></table>
Sized forms (<code>"i4"</code>, <code>"f8"</code>, <code>"c16"</code>), lowercase names (<code>"float64"</code>, <code>"int32"</code>,
<code>"complex128"</code>, <code>"datetime64[ns]"</code>) and byte-order prefixes (<code>"<f8"</code>, <code>">i4"</code>,
<code>"=u2"</code>, <code>"|b1"</code>) are all accepted; a non-native prefix (<code>">i4"</code> on this little-endian host) is
KEPT on the descriptor (<xref href="NumSharp.DType.byteorder" data-throw-if-not-resolved="false"></xref> <code>'>'</code>, <xref href="NumSharp.DType.isnative" data-throw-if-not-resolved="false"></xref> false) as NumPy does.
Deliberately narrowed to NumSharp's capability (the same narrowing as dtype(string)):
only NumSharp's 15 element types can back an array. Three consequences differ from NumPy and are
intentional — (1) complex64 ('F', "c8", "complex64") is rejected with
NotSupportedException (NumSharp has only complex128), as are structured / void / object /
(byte)string dtypes; (2) NumSharp additionally accepts a superset of NumPy's
casing — the C# / NPTypeCode PascalCase names ("Int32", "Single",
"Boolean", "SByte", "Decimal", "Char") that NumPy 2.4.2 rejects — as a
convenience for C# callers; (3) datetime64/timedelta64 descriptors exist (parse, format,
promote, cast-check) but cannot allocate storage yet.
Descriptor surface (mirrors numpy.dtype): type (the C# Type),
typecode (NPTypeCode), Meta, num, name,
kind ('b'/'i'/'u'/'f'/'c'/'M'/'m'), char, itemsize,
alignment, byteorder, str, descr, isbuiltin,
isnative, hasobject, flags, metadata,
newbyteorder(string). Equality is structural (class + byte order + parameters) and null-safe;
Equals(object) additionally coerces a Type, an NPTypeCode or a
dtype string the way np.dtype('i4') == 'i4' does. ToString() is NumPy's str(dtype)
(int32, >i4, datetime64[ns]); ToString(bool) with repr: true is
repr(dtype) (dtype('int32'), dtype('<M8[ns]')).
np.sqrt(x, dtype: typeof(float)); // Type
np.sqrt(x, dtype: NPTypeCode.Single); // NPTypeCode enum
np.sqrt(x, dtype: "float32"); // NumPy string (== NumPy's dtype='float32')
np.sqrt(x, dtype: DType.Single); // DType spelling
np.sqrt(x); // dtype omitted == None / infer
DType d = np.dtype("<f8"); // full descriptor: d.type==typeof(double), d.kind=='f', d.itemsize==8
Type t = DType.Double; // implicit DType -> Type
NPTypeCode c = (DType)"int32"; // implicit string -> DType -> NPTypeCode
bool same = DType.Single == (DType)"f4"; // true — structural equality
DType m = np.dtype("M8[10ns]"); // datetime64[10ns]: m.kind=='M', np.datetime_data(m) == ("ns", 10)
Constructors
DType(NPTypeCode)
Builds a fresh descriptor for an NPTypeCode — a COPY of the class's canonical instance
(prefer From(NPTypeCode) / the implicit conversion, which return the singleton itself).
For the "none/infer" state use a null DType (this ctor throws on
Empty; the implicit NPTypeCode→DType conversion yields null).
public DType(NPTypeCode typecode)
Parameters
typecodeNPTypeCode
DType(Type)
Builds a fresh descriptor for a C# Type — a COPY of the class's canonical instance
(prefer From(Type) / the implicit conversion, which return the singleton itself).
For the "none/infer" state use a null DType (this ctor throws on a
null type; the implicit Type→DType conversion yields null).
public DType(Type type)
Parameters
typeType
Fields
NativeByteOrder
NumPy's '=': the byte order of this host.
public const char NativeByteOrder = '='
Field Value
NotApplicableByteOrder
NumPy's '|': byte order is not applicable (single-byte types).
public const char NotApplicableByteOrder = '|'
Field Value
byteorder
A character indicating the byte-order of this data-type object:
'=' native, '<' little-endian, '>' big-endian, '|' not applicable (single-byte types).
Only the NON-native order is ever stored explicitly (NumPy normalises the host's own order to '=').
public readonly char byteorder
Field Value
itemsize
The size of the dtype in bytes (itemsize).
public readonly int itemsize
Field Value
kind
A character code (one of ‘biufcmMOSUV’) identifying the general kind of data:
b boolean, i signed integer, u unsigned integer, f floating-point,
c complex floating-point, m timedelta, M datetime, O object,
S (byte-)string, U Unicode, V void.
public readonly char kind
Field Value
name
NumPy's dtype.name spelling ("float32", "int64", "bool", "complex128",
"datetime64[ns]") — the CLR Type.Name ("Single") is not a NumPy name and does not
round-trip through dtype(string). The NumSharp-only dtypes have no NumPy analog and get
lowercase names of their own ("decimal", "char") rather than the nearest NumPy stand-in.
public readonly string name
Field Value
type
The C# scalar type of one element (NumPy's dtype.type); null for a class with no C# scalar yet (datetime64 in Stage A).
public readonly Type type
Field Value
typecode
The NumSharp storage discriminator; Empty for a class without storage (datetime64 in Stage A).
public readonly NPTypeCode typecode
Field Value
Properties
Boolean
The bool descriptor.
public static DType Boolean { get; }
Property Value
Byte
The byte (uint8) descriptor.
public static DType Byte { get; }
Property Value
Char
The char descriptor.
public static DType Char { get; }
Property Value
Complex
The Complex (complex128) descriptor.
public static DType Complex { get; }
Property Value
DatetimeMetadata
The instance parameters of a datetime64/timedelta64 descriptor (NumPy's c_metadata); null for every other class.
public DatetimeMetaData? DatetimeMetadata { get; }
Property Value
Decimal
The decimal descriptor.
public static DType Decimal { get; }
Property Value
Double
The double (float64) descriptor.
public static DType Double { get; }
Property Value
Half
The Half (float16) descriptor.
public static DType Half { get; }
Property Value
HostByteOrder
The explicit character of this host's byte order ('<' on little-endian machines) — what str renders for a native descriptor.
public static char HostByteOrder { get; }
Property Value
Int16
The short descriptor.
public static DType Int16 { get; }
Property Value
Int32
The int descriptor.
public static DType Int32 { get; }
Property Value
Int64
The long descriptor.
public static DType Int64 { get; }
Property Value
Meta
The DType CLASS this descriptor instantiates (type(np.dtype('f8'))): np.dtypes.Float64DType.
public DTypeMeta Meta { get; }
Property Value
SByte
The sbyte (int8) descriptor.
public static DType SByte { get; }
Property Value
Single
The float (float32) descriptor.
public static DType Single { get; }
Property Value
SwappedByteOrder
The character of the OTHER byte order ('>' on little-endian machines) — what a non-native descriptor carries.
public static char SwappedByteOrder { get; }
Property Value
UInt16
The ushort descriptor.
public static DType UInt16 { get; }
Property Value
UInt32
The uint descriptor.
public static DType UInt32 { get; }
Property Value
UInt64
The ulong descriptor.
public static DType UInt64 { get; }
Property Value
alignment
The required alignment (bytes) of this data-type (dtype.alignment).
public int alignment { get; }
Property Value
base
dtype.base: the base of a sub-array dtype — this descriptor itself.
public DType @base { get; }
Property Value
char
A unique character code for each of the built-in types ('?' 'b' 'B' 'h' 'H' 'i' 'I' 'l' 'L' 'e' 'f' 'd' 'D' 'M' 'm').
public char @char { get; }
Property Value
descr
The array-interface description (dtype.descr): one unnamed field carrying str — [('', '<i4')].
public IReadOnlyList<(string name, string typestr)> descr { get; }
Property Value
fields
dtype.fields: always null (no structured dtypes).
public IReadOnlyDictionary<string, (DType, int)> fields { get; }
Property Value
flags
dtype.flags: the item flags (NumPy's NPY_ITEM_REFCOUNT | NPY_NEEDS_INIT | … = 63 for a reference-holding dtype, 0 otherwise).
public int flags { get; }
Property Value
hasobject
dtype.hasobject: whether items hold references that need clearing (false for every current class).
public bool hasobject { get; }
Property Value
isbuiltin
dtype.isbuiltin: 1 for the canonical instance of a builtin class, 2 for a user-defined class (the
NumSharp-only Decimal/Char), 0 for any other instance — a datetime descriptor, a non-native byte order, or a
copy made with the public constructors.
public int isbuiltin { get; }
Property Value
isnative
dtype.isnative: true unless the byte order is explicitly the non-host one.
public bool isnative { get; }
Property Value
metadata
dtype.metadata: user metadata (never set in NumSharp — always null).
public object metadata { get; }
Property Value
names
dtype.names: always null (no structured dtypes).
public string[] names { get; }
Property Value
- string[]
ndim
dtype.ndim: the sub-array rank — always 0.
public int ndim { get; }
Property Value
num
NumPy's unique type number (dtype.num).
public int num { get; }
Property Value
shape
dtype.shape: the sub-array shape — always empty (no sub-array dtypes).
public int[] shape { get; }
Property Value
- int[]
str
The array-interface typestring (dtype.str): explicit byte order, kind and size — "<i4",
"|b1", ">f8", "<M8[ns]", "<m8".
public string str { get; }
Property Value
subdtype
dtype.subdtype: always null (no sub-array dtypes).
public (DType, int[])? subdtype { get; }
Property Value
Methods
Equals(DType)
Structural equality: the same class, byte order and instance parameters (M8[ns] != M8[s], >i4 != i4).
public bool Equals(DType other)
Parameters
otherDType
Returns
Equals(NPTypeCode)
Coercing equality with an NPTypeCode; Empty is unequal.
public bool Equals(NPTypeCode typecode)
Parameters
typecodeNPTypeCode
Returns
Equals(object)
NumPy's coercing dtype.eq: the operand is converted to a descriptor first, so a
Type (typeof(int)), an NPTypeCode or a dtype string
("i4", "int32") compare equal to the descriptor they denote; anything that is not a dtype
(a bad string, a DTypeMeta class object, null) compares unequal without raising.
public override bool Equals(object obj)
Parameters
objobject
Returns
Equals(string)
Coercing equality with a dtype STRING (np.dtype('i4') == 'i4'): an unparseable string is simply unequal
(np.dtype('i4') == 'garbage' is False, not an error). This overload exists because a string would
otherwise bind Equals(DType) through the implicit string→DType conversion, which
raises on a bad string.
public bool Equals(string dtype)
Parameters
dtypestring
Returns
Equals(Type)
Coercing equality with a C# Type (np.dtype('i4') == np.int32); a non-NumSharp type is unequal.
public bool Equals(Type type)
Parameters
typeType
Returns
From(NPTypeCode)
The descriptor of an NPTypeCode — the class's canonical instance.
public static DType From(NPTypeCode typecode)
Parameters
typecodeNPTypeCode
Returns
From(string)
Builds a descriptor from a NumPy dtype string (NumPy's case-sensitive grammar). Same as dtype(string).
public static DType From(string dtype)
Parameters
dtypestring
Returns
From(Type)
The descriptor of a C# Type — the class's canonical instance (np.dtype(typeof(int))).
public static DType From(Type type)
Parameters
typeType
Returns
GetHashCode()
Consistent with Equals(DType): the class and byte order, not the parameters (NumPy: hash(M8[ns]) == hash(M8[s])).
public override int GetHashCode()
Returns
GetTypeCode()
Returns this descriptor's NPTypeCode. Combined with a nullable DType
this keeps the old Type dtype idiom drop-in: dtype?.GetTypeCode() yields
NPTypeCode? (null for the none/infer state where dtype == null).
public NPTypeCode GetTypeCode()
Returns
Exceptions
- NotSupportedException
The class has no storage yet (a datetime64/timedelta64 descriptor in Stage A).
ToString()
NumPy's str(dtype): the name for a native descriptor (int32, datetime64[ns],
decimal), the typestring for a non-native or flexible one (>i4).
public override string ToString()
Returns
ToString(bool)
repr false: str(dtype). repr true: NumPy's repr(dtype) —
dtype('int32'), dtype('bool'), dtype('>i4'), dtype('<M8[ns]'), and for the
NumSharp-only user-range classes NumPy's unquoted user form dtype(decimal).
public string ToString(bool repr)
Parameters
reprbool
Returns
newbyteorder(char)
See newbyteorder(string).
public DType newbyteorder(char new_order)
Parameters
new_orderchar
Returns
newbyteorder(string)
Return a new dtype with a different byte order (NumPy's dtype.newbyteorder).
public DType newbyteorder(string new_order = "S")
Parameters
new_orderstringByte order to force. The default (
'S') swaps the current byte order. Codes (first letter, case-insensitive):'S'swap;'<'/'L'/'little'little-endian;'>'/'B'/'big'big-endian;'='/'N'/'native'native;'|'/'I'/'ignore'no change.
Returns
- DType
A new descriptor with the requested byte order (single-byte classes are unaffected).
Exceptions
- ValueError
byteorder not recognized (got '…').
Operators
operator ==(DType, DType)
Structural equality (null-safe).
public static bool operator ==(DType left, DType right)
Parameters
Returns
explicit operator Type(DType)
A descriptor converts back to its Type (none/null ⇒ null) — EXPLICITLY:
(Type)a.dtype, or the NumPy field a.dtype.type. It is deliberately not implicit, because
Type declares its own ==: with conversions in BOTH directions,
a.dtype == typeof(double) would be ambiguous between Type.== and DType.== (CS0034), and adding
==(DType, Type) overloads instead would make descr == null ambiguous (neither target is better). With
only the Type→DType direction implicit, a.dtype == typeof(double),
typeof(double) == a.dtype, Assert.AreEqual(typeof(double), a.dtype) (T infers as DType)
and a.dtype.Should().Be(typeof(double)) all resolve to the structural, coercing DType equality.
public static explicit operator Type(DType dtype)
Parameters
dtypeDType
Returns
Exceptions
- NotSupportedException
The class has no C# storage type yet (datetime64/timedelta64 in Stage A).
operator >(DType, DType)
NumPy's dtype > other: not equal, and right casts safely to left.
public static bool operator >(DType left, DType right)
Parameters
Returns
operator >=(DType, DType)
NumPy's dtype >= other: right casts safely to left.
public static bool operator >=(DType left, DType right)
Parameters
Returns
implicit operator NPTypeCode(DType)
A descriptor converts back to its NPTypeCode (none/null ⇒ Empty).
public static implicit operator NPTypeCode(DType dtype)
Parameters
dtypeDType
Returns
Exceptions
- NotSupportedException
The class has no storage yet (datetime64/timedelta64 in Stage A).
implicit operator DType(NPTypeCode)
An NPTypeCode converts to its canonical descriptor (Empty ⇒ none).
public static implicit operator DType(NPTypeCode typecode)
Parameters
typecodeNPTypeCode
Returns
implicit operator DType(NPTypeCode?)
A nullable NPTypeCode converts to a descriptor (null/Empty ⇒ none).
public static implicit operator DType(NPTypeCode? typecode)
Parameters
typecodeNPTypeCode?
Returns
implicit operator DType(string)
A NumPy dtype string converts to a descriptor via dtype(string) — NumPy's exact,
case-sensitive spelling ("f4", "float32", "<f8", "M8[ns]"). null ⇒ none.
public static implicit operator DType(string dtype)
Parameters
dtypestring
Returns
implicit operator DType(Type)
public static implicit operator DType(Type type)
Parameters
typeType
Returns
operator !=(DType, DType)
Structural inequality (null-safe).
public static bool operator !=(DType left, DType right)
Parameters
Returns
operator <(DType, DType)
NumPy's dtype < other: strictly promotes — not equal, and left casts safely to right.
public static bool operator <(DType left, DType right)
Parameters
Returns
operator <=(DType, DType)
NumPy's dtype <= other: left casts safely to right.
public static bool operator <=(DType left, DType right)