Eğitim
Modül
C# dilinde Dizileri ve foreach deyimini kullanarak veri dizilerini depolama ve yineleme - Training
Dizi değişkenleri oluşturmayı ve dizi öğeleri arasında yineleme yapmayı öğrenin.
Bu tarayıcı artık desteklenmiyor.
En son özelliklerden, güvenlik güncelleştirmelerinden ve teknik destekten faydalanmak için Microsoft Edge’e yükseltin.
Bu makalede, birden çok arabellekte çalışan verilerin okunmasında yardımcı olan türlere genel bir bakış sağlanır. Bunlar öncelikli olarak nesneleri desteklemek PipeReader için kullanılır.
System.Buffers.IBufferWriter<T> zaman uyumlu arabelleğe alınan yazma için yapılan bir sözleşmedir. En düşük düzeyde, arabirim:
Memory<T>
veya Span<T>
öğesine yazılabilir ve kaç T
öğenin yazıldığını belirleyebilirsiniz.void WriteHello(IBufferWriter<byte> writer)
{
// Request at least 5 bytes.
Span<byte> span = writer.GetSpan(5);
ReadOnlySpan<char> helloSpan = "Hello".AsSpan();
int written = Encoding.ASCII.GetBytes(helloSpan, span);
// Tell the writer how many bytes were written.
writer.Advance(written);
}
Yukarıdaki yöntem:
IBufferWriter<byte>
GetSpan(5)
.Span<byte>
öğesine yazar.Bu yazma yöntemi tarafından sağlanan arabelleği kullanırMemory<T>
Span<T>
/.IBufferWriter<T>
Alternatif olarak, Write uzantı yöntemi var olan bir arabelleği öğesine kopyalamak için IBufferWriter<T>
kullanılabilir. Write
arama GetSpan
/Advance
işini uygun şekilde yapar, bu nedenle yazdıktan sonra arama Advance
yapmanıza gerek yoktur:
void WriteHello(IBufferWriter<byte> writer)
{
byte[] helloBytes = Encoding.ASCII.GetBytes("Hello");
// Write helloBytes to the writer. There's no need to call Advance here
// since Write calls Advance.
writer.Write(helloBytes);
}
ArrayBufferWriter<T> , yedekleme deposu tek bir bitişik dizi olan uygulamasının bir uygulamasıdır IBufferWriter<T>
.
GetSpan
ve GetMemory
en az istenen bellek miktarına sahip bir arabellek döndürür. Tam arabellek boyutlarını varsaymayın.Advance
sonra yeni bir arabellek istenmelidir. Daha önce alınan arabelleğe çağrıldıktan sonra Advance
yazılamaz.ReadOnlySequence<T> , öğesinin bitişik veya bitişik olmayan dizisini temsil eden bir yapıdır T
. Şu kaynaklardan oluşturulabilir:
T[]
ReadOnlyMemory<T>
Üçüncü gösterim, üzerinde çeşitli işlemler üzerinde performans etkilerine sahip olduğu için en ilginç temsildir ReadOnlySequence<T>
:
Gösterimi | İşlem | Karmaşıklık |
---|---|---|
T[] /ReadOnlyMemory<T> |
Length |
O(1) |
T[] /ReadOnlyMemory<T> |
GetPosition(long) |
O(1) |
T[] /ReadOnlyMemory<T> |
Slice(int, int) |
O(1) |
T[] /ReadOnlyMemory<T> |
Slice(SequencePosition, SequencePosition) |
O(1) |
ReadOnlySequenceSegment<T> |
Length |
O(1) |
ReadOnlySequenceSegment<T> |
GetPosition(long) |
O(number of segments) |
ReadOnlySequenceSegment<T> |
Slice(int, int) |
O(number of segments) |
ReadOnlySequenceSegment<T> |
Slice(SequencePosition, SequencePosition) |
O(1) |
Bu karma gösterim nedeniyle dizinleri ReadOnlySequence<T>
tamsayı yerine olarak SequencePosition
kullanıma sunar. A SequencePosition
:
ReadOnlySequence<T>
kaynağında temsil eden opak bir değerdir.ReadOnlySequence<T>
bağlıdır., ReadOnlySequence<T>
verileri bir numaralandırılabilir ReadOnlyMemory<T>
olarak kullanıma sunar. Segmentlerin her birini listelemek temel bir foreach kullanılarak yapılabilir:
long FindIndexOf(in ReadOnlySequence<byte> buffer, byte data)
{
long position = 0;
foreach (ReadOnlyMemory<byte> segment in buffer)
{
ReadOnlySpan<byte> span = segment.Span;
var index = span.IndexOf(data);
if (index != -1)
{
return position + index;
}
position += span.Length;
}
return -1;
}
Yukarıdaki yöntem her segmentte belirli bir bayt arar. Her kesimin SequencePosition
izlemeniz gerekiyorsa, ReadOnlySequence<T>.TryGet daha uygundur. Sonraki örnek, önceki kodu tamsayı yerine bir SequencePosition
döndürecek şekilde değiştirir. SequencePosition
döndüren, çağıranın verileri belirli bir dizine almak için ikinci bir taramadan kaçınmasına izin verme avantajına sahiptir.
SequencePosition? FindIndexOf(in ReadOnlySequence<byte> buffer, byte data)
{
SequencePosition position = buffer.Start;
SequencePosition result = position;
while (buffer.TryGet(ref position, out ReadOnlyMemory<byte> segment))
{
ReadOnlySpan<byte> span = segment.Span;
var index = span.IndexOf(data);
if (index != -1)
{
return buffer.GetPosition(index, result);
}
result = position;
}
return null;
}
ve TryGet
birleşimi SequencePosition
bir numaralandırıcı gibi davranır. Konum alanı, her yinelemenin başında içindeki her segmentin ReadOnlySequence<T>
başlangıcı olacak şekilde değiştirilir.
Yukarıdaki yöntem, üzerinde ReadOnlySequence<T>
bir uzantı yöntemi olarak bulunur. PositionOf yukarıdaki kodu basitleştirmek için kullanılabilir:
SequencePosition? FindIndexOf(in ReadOnlySequence<byte> buffer, byte data) => buffer.PositionOf(data);
Veriler dizi içindeki birden çok segmente bölünebileceğinden, bir ReadOnlySequence<T>
işlenme zor olabilir. En iyi performans için kodu iki yola bölün:
Çok segmentli dizilerdeki verileri işlemek için kullanılabilecek birkaç yaklaşım vardır:
SequenceReader<T>
.SequencePosition
izleyerek veri segmentini segmente göre ayrıştırın. Bu, gereksiz ayırmaları önler, ancak özellikle küçük arabellekler için verimsiz olabilir.ReadOnlySequence<T>
bitişik bir diziye kopyalayın ve tek bir arabellek gibi davranın: ReadOnlySequence<T>
küçükse, stackalloc işlecini kullanarak verileri yığın ayrılmış arabelleğe kopyalamak mantıklı olabilir.ReadOnlySequence<T>
havuza alınan bir diziye kopyalayın.ReadOnlySequence<T>.ToArray()
adresini kullanın. Yığında yeni T[]
bir ayırma yaptığı için sık erişimli yollarda bu önerilmez.Aşağıdaki örneklerde işlemeye yönelik bazı yaygın durumlar gösterilmektedir ReadOnlySequence<byte>
:
Aşağıdaki örnek, başından itibaren 4 baytlık büyük endian tamsayı uzunluğunu ayrıştırmaktadır ReadOnlySequence<byte>
.
bool TryParseHeaderLength(ref ReadOnlySequence<byte> buffer, out int length)
{
// If there's not enough space, the length can't be obtained.
if (buffer.Length < 4)
{
length = 0;
return false;
}
// Grab the first 4 bytes of the buffer.
var lengthSlice = buffer.Slice(buffer.Start, 4);
if (lengthSlice.IsSingleSegment)
{
// Fast path since it's a single segment.
length = BinaryPrimitives.ReadInt32BigEndian(lengthSlice.First.Span);
}
else
{
// There are 4 bytes split across multiple segments. Since it's so small, it
// can be copied to a stack allocated buffer. This avoids a heap allocation.
Span<byte> stackBuffer = stackalloc byte[4];
lengthSlice.CopyTo(stackBuffer);
length = BinaryPrimitives.ReadInt32BigEndian(stackBuffer);
}
// Move the buffer 4 bytes ahead.
buffer = buffer.Slice(lengthSlice.End);
return true;
}
Aşağıdaki örnek:
ReadOnlySequence<byte>
ilk yeni satırı (\r\n
) bulur ve out 'line' parametresi aracılığıyla döndürür.\r\n
bu çizgiyi kırpılır.static bool TryParseLine(ref ReadOnlySequence<byte> buffer, out ReadOnlySequence<byte> line)
{
SequencePosition position = buffer.Start;
SequencePosition previous = position;
var index = -1;
line = default;
while (buffer.TryGet(ref position, out ReadOnlyMemory<byte> segment))
{
ReadOnlySpan<byte> span = segment.Span;
// Look for \r in the current segment.
index = span.IndexOf((byte)'\r');
if (index != -1)
{
// Check next segment for \n.
if (index + 1 >= span.Length)
{
var next = position;
if (!buffer.TryGet(ref next, out ReadOnlyMemory<byte> nextSegment))
{
// You're at the end of the sequence.
return false;
}
else if (nextSegment.Span[0] == (byte)'\n')
{
// A match was found.
break;
}
}
// Check the current segment of \n.
else if (span[index + 1] == (byte)'\n')
{
// It was found.
break;
}
}
previous = position;
}
if (index != -1)
{
// Get the position just before the \r\n.
var delimeter = buffer.GetPosition(index, previous);
// Slice the line (excluding \r\n).
line = buffer.Slice(buffer.Start, delimeter);
// Slice the buffer to get the remaining data after the line.
buffer = buffer.Slice(buffer.GetPosition(2, delimeter));
return true;
}
return false;
}
Boş segmentleri içinde ReadOnlySequence<T>
depolamak geçerlidir. Kesimler açıkça numaralandırılıyorken boş segmentler oluşabilir:
static void EmptySegments()
{
// This logic creates a ReadOnlySequence<byte> with 4 segments,
// two of which are empty.
var first = new BufferSegment(new byte[0]);
var last = first.Append(new byte[] { 97 })
.Append(new byte[0]).Append(new byte[] { 98 });
// Construct the ReadOnlySequence<byte> from the linked list segments.
var data = new ReadOnlySequence<byte>(first, 0, last, 1);
// Slice using numbers.
var sequence1 = data.Slice(0, 2);
// Slice using SequencePosition pointing at the empty segment.
var sequence2 = data.Slice(data.Start, 2);
Console.WriteLine($"sequence1.Length={sequence1.Length}"); // sequence1.Length=2
Console.WriteLine($"sequence2.Length={sequence2.Length}"); // sequence2.Length=2
// sequence1.FirstSpan.Length=1
Console.WriteLine($"sequence1.FirstSpan.Length={sequence1.FirstSpan.Length}");
// Slicing using SequencePosition will Slice the ReadOnlySequence<byte> directly
// on the empty segment!
// sequence2.FirstSpan.Length=0
Console.WriteLine($"sequence2.FirstSpan.Length={sequence2.FirstSpan.Length}");
// The following code prints 0, 1, 0, 1.
SequencePosition position = data.Start;
while (data.TryGet(ref position, out ReadOnlyMemory<byte> memory))
{
Console.WriteLine(memory.Length);
}
}
class BufferSegment : ReadOnlySequenceSegment<byte>
{
public BufferSegment(Memory<byte> memory)
{
Memory = memory;
}
public BufferSegment Append(Memory<byte> memory)
{
var segment = new BufferSegment(memory)
{
RunningIndex = RunningIndex + Memory.Length
};
Next = segment;
return segment;
}
}
Yukarıdaki kod, boş kesimler içeren bir ReadOnlySequence<byte>
oluşturur ve bu boş segmentlerin çeşitli API'leri nasıl etkilediğini gösterir:
ReadOnlySequence<T>.Slice
boş bir SequencePosition
kesime işaret eden bir kesim ile bu segment korunur.ReadOnlySequence<T>.Slice
int ile boş kesimleri atlar.ReadOnlySequence<T>
boş kesimler numaralandırılıyor.Normal ile normal karşıtlık yaparken ReadOnlySequence<T>
SequencePosition
///int
ReadOnlySpan<T>
ReadOnlyMemory<T>
/T[]
birkaç olağan dışı sonuç vardır:
SequencePosition
, mutlak bir konum için değil, belirli ReadOnlySequence<T>
bir konum işaretçisidir. Belirli ReadOnlySequence<T>
bir öğesine göre olduğundan, kaynağın dışında ReadOnlySequence<T>
kullanıldığında bir anlamı yoktur.ReadOnlySequence<T>
üzerinde SequencePosition
gerçekleştirilemez. Bu, gibi position++
temel şeyler yapmanın yazıldığını position = ReadOnlySequence<T>.GetPosition(1, position)
gösterir.GetPosition(long)
negatif dizinleri desteklemez. Bu, tüm segmentlerde yürümeden ikinci karakteri son karaktere getirmek mümkün olmadığı anlamına gelir.SequencePosition
karşılaştırılamaz ve bu da şunları zorlaştırıyor: ReadOnlySequence<T>
bir nesne başvurusundan daha büyüktür ve mümkün olduğunca içinde veya başvurusu tarafından geçirilmelidir. ReadOnlySequence<T>
Geçiş veya in
ref
yapının kopyalarını azaltır.ReadOnlySequence<T>
geçerlidir.ReadOnlySequence<T>.TryGet
yinelenirken görüntülenebilir.SequencePosition
kullanarak sırayı ReadOnlySequence<T>.Slice()
dilimleyerek görüntülenebilir.ReadOnlySequence<T>
türdür.ReadOnlySequence<T>
ReadOnlySequence<T>
arasındaki farkları bir arada sunar.byte
ve char
) okumak için yardımcılar sağlar.hem ikili hem de sınırlandırılmış verileri işlemek için yerleşik yöntemler vardır. Aşağıdaki bölümde, aynı yöntemlerin ile nasıl göründüğü gösterilmektedir SequenceReader<T>
:
SequenceReader<T>
doğrudan içinde verileri listelemek için yöntemlere ReadOnlySequence<T>
sahiptir. Aşağıdaki kod, bir'i bir kerede işleme ReadOnlySequence<byte>
byte
örneğidir:
while (reader.TryRead(out byte b))
{
Process(b);
}
, CurrentSpan
yöntemde el ile yapılana benzer şekilde geçerli kesimin Span
öğesini kullanıma sunar.
Aşağıdaki kod, uygulamasının kullanımına yönelik FindIndexOf
örnek bir uygulamadır SequenceReader<T>
:
SequencePosition? FindIndexOf(in ReadOnlySequence<byte> buffer, byte data)
{
var reader = new SequenceReader<byte>(buffer);
while (!reader.End)
{
// Search for the byte in the current span.
var index = reader.CurrentSpan.IndexOf(data);
if (index != -1)
{
// It was found, so advance to the position.
reader.Advance(index);
return reader.Position;
}
// Skip the current segment since there's nothing in it.
reader.Advance(reader.CurrentSpan.Length);
}
return null;
}
Aşağıdaki örnek, başından itibaren 4 baytlık büyük endian tamsayı uzunluğunu ayrıştırmaktadır ReadOnlySequence<byte>
.
bool TryParseHeaderLength(ref ReadOnlySequence<byte> buffer, out int length)
{
var reader = new SequenceReader<byte>(buffer);
return reader.TryReadBigEndian(out length);
}
static ReadOnlySpan<byte> NewLine => new byte[] { (byte)'\r', (byte)'\n' };
static bool TryParseLine(ref ReadOnlySequence<byte> buffer,
out ReadOnlySequence<byte> line)
{
var reader = new SequenceReader<byte>(buffer);
if (reader.TryReadTo(out line, NewLine))
{
buffer = buffer.Slice(reader.Position);
return true;
}
line = default;
return false;
}
SequenceReader<T>
Değiştirilebilir bir yapı olduğundan, her zaman başvuru ile geçirilmelidir.SequenceReader<T>
bir başvuru yapısıdır , bu nedenle yalnızca zaman uyumlu yöntemlerde kullanılabilir ve alanlarda depolanamaz. Daha fazla bilgi için bkz . Ayırmalardan kaçınma.SequenceReader<T>
, yalnızca ileriye doğru okuyucu olarak kullanılmak üzere en iyi duruma getirilmiştir. Rewind
, Read
Peek
ve IsNext
API'leri kullanılarak ele alınamaz küçük yedeklemeler için tasarlanmıştır..NET geri bildirimi
.NET, açık kaynak bir projedir. Geri bildirim sağlamak için bir bağlantı seçin:
Eğitim
Modül
C# dilinde Dizileri ve foreach deyimini kullanarak veri dizilerini depolama ve yineleme - Training
Dizi değişkenleri oluşturmayı ve dizi öğeleri arasında yineleme yapmayı öğrenin.