Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Indexelőket akkor definiálhat, ha egy osztály vagy szerkezet példányai tömbként vagy más gyűjteményként indexelhetők. Az indexelt érték típus vagy példánytag explicit megadása nélkül állítható be vagy kérhető le. Az indexelők a tulajdonságokra hasonlítanak, azzal a kivételével, hogy a tartozékaik paramétereket vesznek fel.
Az alábbi példa egy általános osztályt határoz meg az értékek hozzárendelésére és get
lekérésére szolgáló kiegészítő metódusokkalset
.
namespace Indexers;
public class SampleCollection<T>
{
// Declare an array to store the data elements.
private T[] arr = new T[100];
// Define the indexer to allow client code to use [] notation.
public T this[int i]
{
get => arr[i];
set => arr[i] = value;
}
}
Az előző példában egy olvasási/írási indexelő látható. Tartalmazza mind a get
, mind a set
hozzáférési eszközöket. Csak olvasható indexelőket definiálhat kifejezés alapú tagként, ahogy az a következő példákban látható:
namespace Indexers;
public class ReadOnlySampleCollection<T>(params IEnumerable<T> items)
{
// Declare an array to store the data elements.
private T[] arr = [.. items];
public T this[int i] => arr[i];
}
A get
kulcsszó nincs használatban; =>
bemutatja a kifejezés törzsét.
Az indexelők engedélyezik az indexelt tulajdonságokat: egy vagy több argumentum használatával hivatkozott tulajdonságok. Ezek az argumentumok indexet biztosítanak néhány értékgyűjteményhez.
- Az indexelők lehetővé teszik, hogy az objektumok a tömbökhöz hasonlóan indexelhetők legyenek.
- A
get
tartozék egy értéket ad vissza. Aset
tartozék értékeket rendel hozzá. - A
this
kulcsszó határozza meg az indexelőt. - A
value
kulcsszó aset
hozzáférő argumentuma. - Az indexelőknek nincs szükségük egész indexértékre; Önön múlik, hogyan határozhatja meg az adott keresési mechanizmust.
- Az indexelők túlterhelhetők.
- Az indexelők egy vagy több formális paraméterrel is rendelkezhetnek, például kétdimenziós tömb elérésekor.
- Az indexelőket típusokban
partial
deklarálhatjapartial
.
Szinte mindent alkalmazhat, amit a tulajdonságok használata során tanult az indexelőkre. A szabály egyetlen kivétele a tulajdonságok automatikus implementálása. A fordító nem mindig tudja létrehozni az indexelők számára a megfelelő tárterületet. Egy típuson több indexelőt is definiálhat, ha az egyes indexelők argumentumlistái egyediek.
Indexelők használata
Az indexelőket a saját típusában határozza meg, amikor az API valamilyen gyűjteményt modellez. Az indexelőnek nem szükséges közvetlenül leképeznie a .NET Core keretrendszer részét képező gyűjteménytípusokat. Az indexelők lehetővé teszik, hogy a típus absztrakciójának megfelelő API-t adja meg anélkül, hogy felfedi az absztrakció értékeinek tárolási vagy számítási módját.
Tömbök és vektorok
A típus tömböt vagy vektort modellezhet. A saját indexelő létrehozásának előnye, hogy az igényeinek megfelelően meghatározhatja a gyűjtemény tárterületét. Képzeljen el egy forgatókönyvet, amelyben a típus olyan előzményadatokat modell, amelyek túl nagyok ahhoz, hogy egyszerre betöltse a memóriába. A használat alapján be kell töltenie és ki kell ürítenie a gyűjtemény szakaszait. Az alábbi példa ezt a viselkedést modellozza. Jelenti, hogy hány adatpont létezik. Oldalakat hoz létre az adatok szakaszainak igény szerinti tárolásához. Eltávolítja az oldalakat a memóriából, hogy helyet biztosítson a legutóbbi kérések által igényelt lapoknak.
namespace Indexers;
public record Measurements(double HiTemp, double LoTemp, double AirPressure);
public class DataSamples
{
private class Page
{
private readonly List<Measurements> pageData = new ();
private readonly int _startingIndex;
private readonly int _length;
public Page(int startingIndex, int length)
{
_startingIndex = startingIndex;
_length = length;
// This stays as random stuff:
var generator = new Random();
for (int i = 0; i < length; i++)
{
var m = new Measurements(HiTemp: generator.Next(50, 95),
LoTemp: generator.Next(12, 49),
AirPressure: 28.0 + generator.NextDouble() * 4
);
pageData.Add(m);
}
}
public bool HasItem(int index) =>
((index >= _startingIndex) &&
(index < _startingIndex + _length));
public Measurements this[int index]
{
get
{
LastAccess = DateTime.Now;
return pageData[index - _startingIndex];
}
set
{
pageData[index - _startingIndex] = value;
Dirty = true;
LastAccess = DateTime.Now;
}
}
public bool Dirty { get; private set; } = false;
public DateTime LastAccess { get; set; } = DateTime.Now;
}
private readonly int _totalSize;
private readonly List<Page> pagesInMemory = new ();
public DataSamples(int totalSize)
{
this._totalSize = totalSize;
}
public Measurements this[int index]
{
get
{
if (index < 0) throw new IndexOutOfRangeException("Cannot index less than 0");
if (index >= _totalSize) throw new IndexOutOfRangeException("Cannot index past the end of storage");
var page = updateCachedPagesForAccess(index);
return page[index];
}
set
{
if (index < 0) throw new IndexOutOfRangeException("Cannot index less than 0");
if (index >= _totalSize) throw new IndexOutOfRangeException("Cannot index past the end of storage");
var page = updateCachedPagesForAccess(index);
page[index] = value;
}
}
private Page updateCachedPagesForAccess(int index)
{
foreach (var p in pagesInMemory)
{
if (p.HasItem(index))
{
return p;
}
}
var startingIndex = (index / 1000) * 1000;
var newPage = new Page(startingIndex, 1000);
addPageToCache(newPage);
return newPage;
}
private void addPageToCache(Page p)
{
if (pagesInMemory.Count > 4)
{
// remove oldest non-dirty page:
var oldest = pagesInMemory
.Where(page => !page.Dirty)
.OrderBy(page => page.LastAccess)
.FirstOrDefault();
// Note that this may keep more than 5 pages in memory
// if too much is dirty
if (oldest != null)
pagesInMemory.Remove(oldest);
}
pagesInMemory.Add(p);
}
}
Ezt a tervezési idiómát követve bármilyen gyűjteményt modellezhet, ahol jó oka van annak, hogy a teljes adatkészletet ne töltse be egy memórián belüli gyűjteménybe. Figyelje meg, hogy az Page
osztály egy privát beágyazott osztály, amely nem része a nyilvános felületnek. Ezek a részletek el vannak rejtve az osztály felhasználói elől.
Szótárak
Egy másik gyakori forgatókönyv az, amikor egy szótárt vagy egy térképet kell modellezni. Ez a forgatókönyv az, amikor a típus kulcs, esetleg szövegkulcsok alapján tárolja az értékeket. Ez a példa létrehoz egy szótárat, amely parancssori argumentumokat képez le a beállításokat kezelő Lambda-kifejezésekhez . Az alábbi példa két osztályt mutat be: egy ArgsActions
osztályt, amely leképez egy parancssori beállítást egy System.Action delegálthoz, és egy ArgsProcessor
olyan osztályt, amely mindegyik végrehajtásához ArgsActions
használja, amikor találkozik ezzel a Action beállítással.
namespace Indexers;
public class ArgsProcessor
{
private readonly ArgsActions _actions;
public ArgsProcessor(ArgsActions actions)
{
_actions = actions;
}
public void Process(string[] args)
{
foreach (var arg in args)
{
_actions[arg]?.Invoke();
}
}
}
public class ArgsActions
{
readonly private Dictionary<string, Action> _argsActions = new();
public Action this[string s]
{
get
{
Action? action;
Action defaultAction = () => { };
return _argsActions.TryGetValue(s, out action) ? action : defaultAction;
}
}
public void SetOption(string s, Action a)
{
_argsActions[s] = a;
}
}
Ebben a példában a ArgsAction
gyűjtemény szorosan megfelel az alapul szolgáló gyűjteménynek. Ez get
határozza meg, hogy egy adott beállítás konfigurálva van-e. Ha igen, akkor az Action adott beállításhoz társított értéket adja vissza. Ha nem, akkor Action egy semmit nem csináló értéket ad vissza. A nyilvános elérési metódus nem tartalmaz set
elérési metódust. Ehelyett a kialakítás egy nyilvános módszert használ a beállítások megadásához.
Többdimenziós térképek
Több argumentumot használó indexelőket is létrehozhat. Ezen kívül ezek az argumentumok nincsenek kötve ahhoz, hogy azonos típusúak legyenek.
Az alábbi példa egy mandelbrotkészlet értékeit generáló osztályt mutat be. A készlet mögötti matematikával kapcsolatos további információkért olvassa el ezt a cikket. Az indexelő két double típus használatával határoz meg egy pontot az X, Y síkban. A get
hozzáférő kiszámítja az iterációk számát, amíg meg nem állapítja, hogy egy pont nem része a készletnek. Az iterációk maximális számának elérésekor a pont a halmazban van, és az osztály maxIterations értéke lesz visszaadva. A Mandelbrot-halmazhoz kapcsolódó, számítógéppel generált képek határozzák meg az iterációk számához tartozó színeket, amelyek annak eldöntéséhez szükségesek, hogy egy pont a halmazon kívül helyezkedik el.
namespace Indexers;
public class Mandelbrot(int maxIterations)
{
public int this[double x, double y]
{
get
{
var iterations = 0;
var x0 = x;
var y0 = y;
while ((x * x + y * y < 4) &&
(iterations < maxIterations))
{
(x, y) = (x * x - y * y + x0, 2 * x * y + y0);
iterations++;
}
return iterations;
}
}
}
A Mandelbrot-készlet a valós számértékek minden (x,y) koordinátáján meghatároz értékeket. Ez meghatároz egy olyan szótárt, amely végtelen számú értéket tartalmazhat. Ezért nincs tároló a szett mögött. Ehelyett ez az osztály kiszámítja az egyes pontok értékét, amikor a kód meghívja a tartozékot get
. Nincs mögöttes tároló.
Összegzés
Az indexelőket bármikor létrehozhatja, amikor tulajdonságszerű elem van az osztályban, ahol ez a tulajdonság nem egyetlen értéket, hanem értékhalmazt jelöl. Egy vagy több argumentum azonosítja az egyes elemeket. Ezek az argumentumok egyedileg azonosítják, hogy a halmaz melyik elemére kell hivatkozni. Az indexelők kibővítik a tulajdonságok fogalmát, ahol a rendszer egy tagot az osztályon kívülről származó adatelemként kezel, de belülről mint egy metódust. Az indexelők lehetővé teszik, hogy az argumentumok egyetlen elemet keressenek egy olyan tulajdonságban, amely egy elemkészletet jelöl.
A mintamappa indexelők számára elérhető. A letöltési utasításokért tekintse meg példákat és oktatóanyagokat.
C# nyelvi specifikáció
További információ: Indexelők a C# nyelvi specifikációjában. A nyelvi specifikáció a C#-szintaxis és -használat végleges forrása.