Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Not
Bu makale bir özellik belirtimidir. Belirtim, özelliğin tasarım belgesi olarak görev alır. Önerilen belirtim değişikliklerini ve özelliğin tasarımı ve geliştirilmesi sırasında gereken bilgileri içerir. Bu makaleler, önerilen belirtim değişiklikleri son haline getirilene ve geçerli ECMA belirtimine dahil edilene kadar yayımlanır.
Özellik belirtimi ile tamamlanan uygulama arasında bazı tutarsızlıklar olabilir. Bu farklılıklar, ilgili dil tasarım toplantısı (LDM) notlarındakayıt altına alınmıştır.
Özellik belirtimlerini C# dil standardına benimseme işlemi hakkında daha fazla bilgi edinmek için
Şampiyon sorunu: https://github.com/dotnet/csharplang/issues/435
Özet
Yerel boyutlu imzalı ve imzasız tamsayı türleri için dil desteği.
Motivasyon birlikte çalışma senaryoları ve düşük düzeyli kitaplıklar içindir.
Tasarım
nint
ve nuint
tanımlayıcıları, yerel imzalı ve imzasız tamsayı türlerini temsil eden yeni bağlamsal anahtar sözcüklerdir.
Tanımlayıcılar yalnızca ad araması bu program konumunda uygun bir sonuç bulamadıklarında anahtar sözcük olarak değerlendirilir.
nint x = 3;
_ = nint.Equals(x, 3);
nint
ve nuint
türleri, derleyicinin bu türler için ek dönüştürmeler ve işlemler sağlayarak onları yerel tamsayılar olarak ön plana çıkardığı System.IntPtr
ve System.UIntPtr
temel türleri tarafından temsil edilir.
Sabit
Sabit ifadeler nint
veya nuint
türünde olabilir.
Yerel int değişmez değerleri için doğrudan söz dizimi yoktur. Bunun yerine diğer tamsayı sabit değerlerinin örtük veya açık atamaları kullanılabilir: const nint i = (nint)42;
.
nint
sabitleri [ int.MinValue
, int.MaxValue
] aralığındadır.
nuint
sabitleri [ uint.MinValue
, uint.MaxValue
] aralığındadır.
MinValue
veya MaxValue
nint
veya nuint
alanı yoktur çünkü nuint.MinValue
dışında bu değerler sabit olarak yayılamaz.
Sabit katlama, tüm tekli işleçler { +
, -
, ~
} ve ikili işleçler { +
, -
, *
, /
, %
, ==
, !=
, <
, <=
, >
, >=
, &
, |
, ^
, <<
, >>
} için desteklenir.
Sabit katlama işlemleri, derleyici platformundan bağımsız olarak tutarlı davranış sağlamak amacıyla yerel int türleri yerine Int32
ve UInt32
işlenenleri kullanılarak değerlendirilir.
İşlem 32 bit sabit değerle sonuçlanırsa, derleme zamanında sabit katlama gerçekleştirilir.
Aksi takdirde işlem çalışma zamanında yürütülür ve sabit olarak kabul edilmez.
Dönüşüm
nint
ile IntPtr
arasında ve nuint
ile UIntPtr
arasında bir kimlik dönüştürmesi vardır.
Yerel tamsayılar ve yalnızca temel alınan türlere göre farklılık gösteren bileşik türler arasında bir kimlik dönüşümü vardır: diziler, Nullable<>
, oluşturulan türler ve demetler.
Aşağıdaki tablolar özel türler arasındaki dönüştürmeleri kapsar.
(Her dönüştürme için IL, farklıysa unchecked
ve checked
bağlamları için varyantları içerir.)
Aşağıdaki tabloda genel notlar:
-
conv.u
, yerel tamsayıya sıfır uzatmalı bir dönüşümdür veconv.i
, yerel tamsayıya işaret uzatmalı bir dönüşümdür. - Hem
checked
hem de daraltma için bağlamları şunlardır:-
conv.ovf.*
içinsigned to *
-
conv.ovf.*.un
içinunsigned to *
-
-
unchecked
için bağlamlar şunlardır:-
conv.i*
içinsigned to *
(burada * hedef genişliktir) -
conv.u*
içinunsigned to *
(burada * hedef genişliktir)
-
-
unchecked
için bağlamlar şunlardır:-
conv.i*
içinany to signed *
(burada * hedef genişliktir) -
conv.u*
içinany to unsigned *
(burada * hedef genişliktir)
-
Birkaç örnek alınıyor:
-
sbyte to nint
vesbyte to nuint
conv.i
kullanırken,byte to nint
vebyte to nuint
conv.u
kullanıyor çünkü bunların hepsi genişletme. -
nint to byte
venuint to byte
conv.u1
kullanırkennint to sbyte
venuint to sbyte
conv.i1
kullanır.byte
,sbyte
,short
veushort
için "yığın türü"int32
. Bu nedenleconv.i1
etkin bir şekilde "imzalı bir bayta indirilir ve ardından int32'ye kadar imzalanır",conv.u1
ise etkin bir şekilde "imzasız bir bayta indirilir ve sonra da sıfırdan int32'ye genişletilir". -
checked void* to nint
,conv.ovf.i.un
'i,checked void* to long
'ninconv.ovf.i8.un
'ü kullandığı şekilde kullanır.
Işlenen | Hedef | Dönüşüm | Illinois |
---|---|---|---|
object |
nint |
Kutudan çıkarma | unbox |
void* |
nint |
PointerToVoid | nop / conv.ovf.i.un |
sbyte |
nint |
Gizli Sayısal | conv.i |
byte |
nint |
Gizli Sayısal | conv.u |
short |
nint |
Gizli Sayısal | conv.i |
ushort |
nint |
Gizli Sayısal | conv.u |
int |
nint |
Gizli Sayısal | conv.i |
uint |
nint |
Açık Sayısal | conv.u / conv.ovf.i.un |
long |
nint |
Açık Sayısal | conv.i / conv.ovf.i |
ulong |
nint |
Açık Sayısal | conv.i / conv.ovf.i.un |
char |
nint |
Gizli Sayısal | conv.u |
float |
nint |
Açık Sayısal | conv.i / conv.ovf.i |
double |
nint |
Açık Sayısal | conv.i / conv.ovf.i |
decimal |
nint |
Açık Sayısal | long decimal.op_Explicit(decimal) conv.i / ... conv.ovf.i |
IntPtr |
nint |
Kimlik | |
UIntPtr |
nint |
Hiç kimse | |
object |
nuint |
Kutudan çıkarma | unbox |
void* |
nuint |
PointerToVoid | Nop |
sbyte |
nuint |
Açık Sayısal | conv.i / conv.ovf.u |
byte |
nuint |
Gizli Sayısal | conv.u |
short |
nuint |
Açık Sayısal | conv.i / conv.ovf.u |
ushort |
nuint |
Gizli Sayısal | conv.u |
int |
nuint |
Açık Sayısal | conv.i / conv.ovf.u |
uint |
nuint |
Gizli Sayısal | conv.u |
long |
nuint |
Açık Sayısal | conv.u / conv.ovf.u |
ulong |
nuint |
Açık Sayısal | conv.u / conv.ovf.u.un |
char |
nuint |
Gizli Sayısal | conv.u |
float |
nuint |
Açık Sayısal | conv.u / conv.ovf.u |
double |
nuint |
Açık Sayısal | conv.u / conv.ovf.u |
decimal |
nuint |
Açık Sayısal | ulong decimal.op_Explicit(decimal) conv.u / ... conv.ovf.u.un |
IntPtr |
nuint |
Hiç kimse | |
UIntPtr |
nuint |
Kimlik | |
Sayım | nint |
Açık Belirtim | |
Sayım | nuint |
Açık Belirtim |
Işlenen | Hedef | Dönüşüm | Illinois |
---|---|---|---|
nint |
object |
Boks | box |
nint |
void* |
PointerToVoid | nop / conv.ovf.u |
nint |
nuint |
Açık Sayısal |
conv.u (atlanabilir) / conv.ovf.u |
nint |
sbyte |
Açık Sayısal | conv.i1 / conv.ovf.i1 |
nint |
byte |
Açık Sayısal | conv.u1 / conv.ovf.u1 |
nint |
short |
Açık Sayısal | conv.i2 / conv.ovf.i2 |
nint |
ushort |
Açık Sayısal | conv.u2 / conv.ovf.u2 |
nint |
int |
Açık Sayısal | conv.i4 / conv.ovf.i4 |
nint |
uint |
Açık Sayısal | conv.u4 / conv.ovf.u4 |
nint |
long |
Gizli Sayısal | conv.i8 |
nint |
ulong |
Açık Sayısal | conv.i8 / conv.ovf.u8 |
nint |
char |
Açık Sayısal | conv.u2 / conv.ovf.u2 |
nint |
float |
Gizli Sayısal | conv.r4 |
nint |
double |
Gizli Sayısal | conv.r8 |
nint |
decimal |
Gizli Sayısal | conv.i8 decimal decimal.op_Implicit(long) |
nint |
IntPtr |
Kimlik | |
nint |
UIntPtr |
Hiç kimse | |
nint |
Sayım | Açık Belirtim | |
nuint |
object |
Boks | box |
nuint |
void* |
PointerToVoid | Nop |
nuint |
nint |
Açık Sayısal |
conv.i (atlanabilir) / conv.ovf.i.un |
nuint |
sbyte |
Açık Sayısal | conv.i1 / conv.ovf.i1.un |
nuint |
byte |
Açık Sayısal | conv.u1 / conv.ovf.u1.un |
nuint |
short |
Açık Sayısal | conv.i2 / conv.ovf.i2.un |
nuint |
ushort |
Açık Sayısal | conv.u2 / conv.ovf.u2.un |
nuint |
int |
Açık Sayısal | conv.i4 / conv.ovf.i4.un |
nuint |
uint |
Açık Sayısal | conv.u4 / conv.ovf.u4.un |
nuint |
long |
Açık Sayısal | conv.u8 / conv.ovf.i8.un |
nuint |
ulong |
Gizli Sayısal | conv.u8 |
nuint |
char |
Açık Sayısal | conv.u2 / conv.ovf.u2.un |
nuint |
float |
Gizli Sayısal | conv.r.un conv.r4 |
nuint |
double |
Gizli Sayısal | conv.r.un conv.r8 |
nuint |
decimal |
Gizli Sayısal | conv.u8 decimal decimal.op_Implicit(ulong) |
nuint |
IntPtr |
Hiç kimse | |
nuint |
UIntPtr |
Kimlik | |
nuint |
Sayım | Açık Belirtim |
A
'den Nullable<B>
dönüştürme:
-
A
'danB
'e bir kimlik dönüşümü veya örtük dönüşüm varsa, örtük null atanabilir dönüşüm; -
A
'denB
'e açık bir dönüştürme varsa, açık bir boş değer atanabilir dönüştürme yapılabilir; - aksi takdirde geçersiz.
Nullable<A>
'den B
dönüştürme:
- Kimlik veya örtük ya da açık sayısal dönüşüm varsa,
A
'denB
'e açık null dönüşümü yapın; - aksi takdirde geçersiz.
Nullable<A>
'den Nullable<B>
dönüştürme:
-
A
'denB
'e bir kimlik dönüşümü varsa; -
A
'denB
'ye örtük veya açık bir sayısal dönüşüm varsa, açık şekilde null atanabilir bir dönüştürme yapılabilir. - aksi takdirde geçersiz.
Işleç
Önceden tanımlanmış işleçler aşağıdaki gibidir.
İşlenenlerden en az biri
(Her işlecin IL'i, unchecked
ve farklıysa checked
bağlamları için varyantları içerir.)
Tekli | İşleç İmzası | Illinois |
---|---|---|
+ |
nint operator +(nint value) |
nop |
+ |
nuint operator +(nuint value) |
nop |
- |
nint operator -(nint value) |
neg |
~ |
nint operator ~(nint value) |
not |
~ |
nuint operator ~(nuint value) |
not |
İkili | İşleç İmzası | Illinois |
---|---|---|
+ |
nint operator +(nint left, nint right) |
add / add.ovf |
+ |
nuint operator +(nuint left, nuint right) |
add / add.ovf.un |
- |
nint operator -(nint left, nint right) |
sub / sub.ovf |
- |
nuint operator -(nuint left, nuint right) |
sub / sub.ovf.un |
* |
nint operator *(nint left, nint right) |
mul / mul.ovf |
* |
nuint operator *(nuint left, nuint right) |
mul / mul.ovf.un |
/ |
nint operator /(nint left, nint right) |
div |
/ |
nuint operator /(nuint left, nuint right) |
div.un |
% |
nint operator %(nint left, nint right) |
rem |
% |
nuint operator %(nuint left, nuint right) |
rem.un |
== |
bool operator ==(nint left, nint right) |
beq / ceq |
== |
bool operator ==(nuint left, nuint right) |
beq / ceq |
!= |
bool operator !=(nint left, nint right) |
bne |
!= |
bool operator !=(nuint left, nuint right) |
bne |
< |
bool operator <(nint left, nint right) |
blt / clt |
< |
bool operator <(nuint left, nuint right) |
blt.un / clt.un |
<= |
bool operator <=(nint left, nint right) |
ble |
<= |
bool operator <=(nuint left, nuint right) |
ble.un |
> |
bool operator >(nint left, nint right) |
bgt / cgt |
> |
bool operator >(nuint left, nuint right) |
bgt.un / cgt.un |
>= |
bool operator >=(nint left, nint right) |
bge |
>= |
bool operator >=(nuint left, nuint right) |
bge.un |
& |
nint operator &(nint left, nint right) |
and |
& |
nuint operator &(nuint left, nuint right) |
and |
| |
nint operator |(nint left, nint right) |
or |
| |
nuint operator |(nuint left, nuint right) |
or |
^ |
nint operator ^(nint left, nint right) |
xor |
^ |
nuint operator ^(nuint left, nuint right) |
xor |
<< |
nint operator <<(nint left, int right) |
shl |
<< |
nuint operator <<(nuint left, int right) |
shl |
>> |
nint operator >>(nint left, int right) |
shr |
>> |
nuint operator >>(nuint left, int right) |
shr.un |
Bazı ikili işleçler için IL işleçleri ek işlenen türlerini destekler (bkz. ECMA-335 III.1.5 İşlenen türü tablosu). Ancak C# tarafından desteklenen operand türleri kümesi, basitlik ve dildeki mevcut operatörlerle tutarlılık açısından sınırlıdır.
Bağımsız değişkenlerin ve dönüş türlerinin nint?
ve nuint?
olduğu işleçlerin yükseltilmiş sürümleri desteklenir.
Bileşik atama işlemleri, x op= y
veya x
yerel int'ler olduğu y
önceden tanımlanmış işleçlere sahip diğer temel türlerle aynı kuralları izler.
Özellikle, ifade x = (T)(x op y)
'in T
türünde olduğu ve x
'ün yalnızca bir kez değerlendirildiği durumlarda x
olarak bağlanır.
Kaydırma işleçleri, sizeof(nint)
4 ise kaydırılacak bit sayısını 5 bit olarak, sizeof(nint)
8 ise 6 bit olarak maskelemelidir.
(C# belirtiminde §12.11) bölümüne bakın).
C#9 derleyicisi, önceki bir dil sürümüyle derlenirken önceden tanımlanmış yerel tamsayı işleçlerine bağlama hatalarını bildirir, ancak yerel tamsayılara ve yerel tamsayılardan önceden tanımlanmış dönüştürmelerin kullanılmasına izin verir.
csc -langversion:9 -t:library A.cs
public class A
{
public static nint F;
}
csc -langversion:8 -r:A.dll B.cs
class B : A
{
static void Main()
{
F = F + 1; // error: nint operator+ not available with -langversion:8
F = (System.IntPtr)F + 1; // ok
}
}
İşaretçi aritmetiği
Yerel tamsayı uzaklıklarına sahip işaretçi ekleme veya çıkarma işlemleri için C# dilinde önceden tanımlanmış işleç yoktur.
Bunun yerine, nint
ve nuint
değerleri long
ve ulong
olarak yükseltilir ve işaretçi aritmetiği bu türler için önceden tanımlanmış işleçler kullanır.
static T* AddLeftS(nint x, T* y) => x + y; // T* operator +(long left, T* right)
static T* AddLeftU(nuint x, T* y) => x + y; // T* operator +(ulong left, T* right)
static T* AddRightS(T* x, nint y) => x + y; // T* operator +(T* left, long right)
static T* AddRightU(T* x, nuint y) => x + y; // T* operator +(T* left, ulong right)
static T* SubRightS(T* x, nint y) => x - y; // T* operator -(T* left, long right)
static T* SubRightU(T* x, nuint y) => x - y; // T* operator -(T* left, ulong right)
İkili sayısal yükseltmeler
ikili sayısal yükseltmeleri bilgilendirici metin (bkz. C# belirtimindeki §12.4.7.3) aşağıdaki gibi güncelleştirilir:
- …
- Aksi takdirde, işlenenlerden biri
ulong
türündeyse, diğer işlenenulong
türüne dönüştürülür veya diğer işlenensbyte
,short
,int
,nint
veyalong
türündeyse bağlama zamanı hatası oluşur.- Aksi takdirde, işlenenlerden biri
nuint
türündeyse, diğer işlenennuint
türüne dönüştürülür veya diğer işlenensbyte
,short
,int
,nint
veyalong
türündeyse bağlama zamanı hatası oluşur.- Aksi halde, operantlardan biri
long
türündeyse, diğer operantlong
türüne dönüştürülür.- Aksi takdirde, işlenenlerden biri
uint
türündeyse ve diğer işlenensbyte
,short
,nint
, veyaint
türündeyse, her iki işlenen delong
türüne dönüştürülür.- Aksi halde, operantlardan biri
uint
türündeyse, diğer operantuint
türüne dönüştürülür.- Aksi takdirde, işlenenlerden biri
nint
türündeyse, diğer işlenennint
türüne dönüştürülür.- Aksi takdirde, her iki işlenen de
int
türüne dönüştürülür.
Dinamik
Dönüştürmeler ve işleçler derleyici tarafından sentezlenir ve temel IntPtr
ve UIntPtr
türlerinin bir parçası değildir.
Sonuç olarak bu dönüştürmeler ve işleçler için çalışma zamanı bağlayıcısından kullanılamaz dynamic
.
nint x = 2;
nint y = x + x; // ok
dynamic d = x;
nint z = d + x; // RuntimeBinderException: '+' cannot be applied 'System.IntPtr' and 'System.IntPtr'
Tür üyeleri
nint
veya nuint
için tek oluşturucu parametresiz oluşturucudur.
Aşağıdaki System.IntPtr
ve System.UIntPtr
üyeleri,nint
veya nuint
'ten açıkça dışındadır:
// constructors
// arithmetic operators
// implicit and explicit conversions
public static readonly IntPtr Zero; // use 0 instead
public static int Size { get; } // use sizeof() instead
public static IntPtr Add(IntPtr pointer, int offset);
public static IntPtr Subtract(IntPtr pointer, int offset);
public int ToInt32();
public long ToInt64();
public void* ToPointer();
System.IntPtr
ve System.UIntPtr
nuint
'e örtük olarak eklenir. .NET Framework 4.7.2 için:
public override bool Equals(object obj);
public override int GetHashCode();
public override string ToString();
public string ToString(string format);
System.IntPtr
ve System.UIntPtr
nuint
örtülü olarak dahildir ve temel türlerin kullanımları karşılık gelen yerel tamsayı türleriyle değiştirilir.
Örneğin, IntPtr
ISerializable, IEquatable<IntPtr>, IComparable<IntPtr>
uygularsa nint
ISerializable, IEquatable<nint>, IComparable<nint>
uygular.
Geçersiz kılma, saklama ve uygulama
nint
ve System.IntPtr
ile nuint
ve System.UIntPtr
geçersiz kılma, gizleme ve uygulama için eşdeğer kabul edilir.
Aşırı yüklemeler yalnızca nint
ve System.IntPtr
ile nuint
ve System.UIntPtr
farklılık gösteremez.
Geçersiz kılmalar ve uygulamalar, yalnızca nint
ve System.IntPtr
veya nuint
ve System.UIntPtr
ile sınırlı olarak farklılık gösterebilir.
Yöntemler, yalnızca nint
ve System.IntPtr
veya nuint
ve System.UIntPtr
ile farklılık gösteren diğer yöntemleri gizler.
Çeşitli
dizi dizinleri olarak kullanılan nint
ve nuint
ifadeleri dönüştürme olmadan yayılır.
static object GetItem(object[] array, nint index)
{
return array[index]; // ok
}
nint
ve nuint
, C# dilinden enum
temel türü olarak kullanılamaz.
enum E : nint // error: byte, sbyte, short, ushort, int, uint, long, or ulong expected
{
}
Okuma ve yazma işlemleri nint
ve nuint
için atomiktir.
Alanlar volatile
ve nint
türleri için nuint
olarak işaretlenebilir.
ECMA-334 15.5.4, temel tür enum
veya System.IntPtr
olan System.UIntPtr
içermez.
default(nint)
ve new nint()
(nint)0
eşdeğerdir; default(nuint)
ve new nuint()
(nuint)0
eşdeğerdir.
typeof(nint)
, typeof(IntPtr)
'dir; typeof(nuint)
, typeof(UIntPtr)
'tür.
sizeof(nint)
ve sizeof(nuint)
desteklenir, ancak güvenli olmayan bir bağlamda derleme gerektirir (sizeof(IntPtr)
ve sizeof(UIntPtr)
için gerektiği gibi).
Değerler derleme zamanı sabitleri değildir.
sizeof(nint)
sizeof(IntPtr)
yerine IntPtr.Size
olarak uygulanır; sizeof(nuint)
sizeof(UIntPtr)
yerine UIntPtr.Size
olarak uygulanır.
Tür başvurularını içeren derleyici tanılamaları, nint
veya nuint
yerine nint
veya nuint
'e ilişkin olarak IntPtr
veya UIntPtr
'ü raporlar.
Meta veriler
nint
ve nuint
meta verilerde System.IntPtr
ve System.UIntPtr
olarak gösterilir.
nint
veya nuint
içeren tür başvuruları, tür başvurusunun hangi bölümlerinin yerel int olduğunu belirtmek için bir System.Runtime.CompilerServices.NativeIntegerAttribute
ile birlikte gönderilir.
namespace System.Runtime.CompilerServices
{
[AttributeUsage(
AttributeTargets.Class |
AttributeTargets.Event |
AttributeTargets.Field |
AttributeTargets.GenericParameter |
AttributeTargets.Parameter |
AttributeTargets.Property |
AttributeTargets.ReturnValue,
AllowMultiple = false,
Inherited = false)]
public sealed class NativeIntegerAttribute : Attribute
{
public NativeIntegerAttribute()
{
TransformFlags = new[] { true };
}
public NativeIntegerAttribute(bool[] flags)
{
TransformFlags = flags;
}
public readonly bool[] TransformFlags;
}
}
NativeIntegerAttribute
ile tür başvurularının kodlaması NativeIntegerAttribute.mdkapsamındadır.
Alternatif
Yukarıdaki "tür silme" yaklaşımının bir alternatifi de yeni türleri tanıtmaktır: System.NativeInt
ve System.NativeUInt
.
public readonly struct NativeInt
{
public IntPtr Value;
}
Ayrı türler, IntPtr
'dan farklı olarak aşırı yüklemeye izin verir ve ayrıca ayrıştırmayı ToString()
'den farklı hale getirebilir.
Ancak CLR'nin bu türleri verimli bir şekilde işlemesi için daha fazla çalışma olacaktır ve bu da özelliğin birincil amacı olan verimliliği yener.
Ayrıca IntPtr
kullanan mevcut yerel int koduyla birlikte çalışmak daha zor olacaktır.
Başka bir alternatif, çerçevedeki IntPtr
için daha yerel int desteği eklemektir ancak belirli bir derleyici desteği olmadan.
Tüm yeni dönüştürmeler ve aritmetik işlemler derleyici tarafından otomatik olarak desteklenir.
Ancak dil anahtar sözcükler, sabitler veya checked
işlemleri sağlamaz.
Tasarım toplantıları
- https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-05-26.md
- https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-06-13.md
- https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-07-05.md#native-int-and-intptr-operators
- https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-10-23.md
- https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-03-25.md
C# feature specifications