Zoeken…


Invoering

C # 7.0 is de zevende versie van C #. Deze versie bevat enkele nieuwe functies: taalondersteuning voor Tuples, lokale functies, out var declaraties, cijferscheidingstekens, binaire literalen, patroonovereenkomst, gooi-expressies, ref return en ref local en uitgebreide ledenlijst met fysieke leden.

Officiële referentie: wat is nieuw in C # 7

var-verklaring

Een gebruikelijk patroon in C # is het gebruik van bool TryParse(object input, out object value) om objecten veilig te ontleden.

De out var aangifte is een eenvoudige functie om de leesbaarheid te verbeteren. Hiermee kan een variabele worden gedeclareerd terwijl deze wordt doorgegeven als een parameter out.

Een variabele die op deze manier wordt gedeclareerd, is gericht op de rest van het lichaam op het punt waarop deze wordt gedeclareerd.

Voorbeeld

Als u TryParse vóór C # 7.0 gebruikt, moet u een variabele declareren om de waarde te ontvangen voordat u de functie TryParse :

7.0
int value;
if (int.TryParse(input, out value)) 
{
    Foo(value); // ok
}
else
{
    Foo(value); // value is zero
}

Foo(value); // ok

In C # 7.0 kunt u de declaratie van de variabele doorgeven die aan de parameter out doorgegeven, waardoor de noodzaak voor een afzonderlijke variabele declaratie is geëlimineerd:

7.0
if (int.TryParse(input, out var value)) 
{
    Foo(value); // ok
}
else
{
    Foo(value); // value is zero
}

Foo(value); // still ok, the value in scope within the remainder of the body

Als sommige van de parameters die een functie terugkeert in out niet nodig is kunt u het zich ontdoen van operator te gebruiken _ .

p.GetCoordinates(out var x, out _); // I only care about x

Een out var verklaring kan worden gebruikt met bestaande functie die reeds out parameters. De syntaxis van de functieverklaring blijft hetzelfde en er zijn geen aanvullende vereisten nodig om de functie compatibel te maken met een out var aangifte. Deze functie is gewoon syntactische suiker.

Een ander kenmerk van onze out var verklaring is dat het kan worden gebruikt met anonieme typen.

7.0
var a = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var groupedByMod2 = a.Select(x => new
                                  {
                                      Source = x,
                                      Mod2 = x % 2
                                  })
                     .GroupBy(x => x.Mod2)
                     .ToDictionary(g => g.Key, g => g.ToArray());
if (groupedByMod2.TryGetValue(1, out var oddElements))
{
    Console.WriteLine(oddElements.Length);
}

In deze code maken we een Dictionary met int sleutel en een reeks anonieme typewaarden. In de vorige versie van C # was het onmogelijk om de TryGetValue methode hier te gebruiken, omdat je de variabele out moest declareren (die van het anonieme type is!). Zonder out var hoeven we echter niet expliciet het type van de out variabele te specificeren.

beperkingen

Merk op dat onze var-verklaringen van beperkt nut zijn in LINQ-query's omdat expressies worden geïnterpreteerd als expressie-lambda-instanties, dus het bereik van de geïntroduceerde variabelen is beperkt tot deze lambdas. De volgende code werkt bijvoorbeeld niet:

var nums = 
    from item in seq
    let success = int.TryParse(item, out var tmp)
    select success ? tmp : 0; // Error: The name 'tmp' does not exist in the current context

Referenties

Binaire literals

Het voorvoegsel 0b kan worden gebruikt om binaire literalen voor te stellen.

Met binaire literalen kunnen getallen uit nullen en enen worden geconstrueerd, wat het gemakkelijker maakt om te zien welke bits zijn ingesteld in de binaire weergave van een getal. Dit kan handig zijn voor het werken met binaire vlaggen.

De volgende zijn equivalente manieren om een int met waarde 34 (= 2 5 + 2 1 ):

// Using a binary literal:
//   bits: 76543210
int a1 = 0b00100010;          // binary: explicitly specify bits

// Existing methods:
int a2 = 0x22;                // hexadecimal: every digit corresponds to 4 bits
int a3 = 34;                  // decimal: hard to visualise which bits are set
int a4 = (1 << 5) | (1 << 1); // bitwise arithmetic: combining non-zero bits

Vlaggen opsommingen

Voorheen kon het opgeven van vlagwaarden voor een enum alleen worden gedaan met behulp van een van de drie methoden in dit voorbeeld:

[Flags]
public enum DaysOfWeek
{
    // Previously available methods:
    //          decimal        hex       bit shifting
    Monday    =  1,    //    = 0x01    = 1 << 0
    Tuesday   =  2,    //    = 0x02    = 1 << 1
    Wednesday =  4,    //    = 0x04    = 1 << 2
    Thursday  =  8,    //    = 0x08    = 1 << 3
    Friday    = 16,    //    = 0x10    = 1 << 4
    Saturday  = 32,    //    = 0x20    = 1 << 5
    Sunday    = 64,    //    = 0x40    = 1 << 6

    Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday,
    Weekends = Saturday | Sunday
}

Met binaire literalen is het duidelijker welke bits zijn ingesteld, en het gebruik ervan vereist geen begrip van hexadecimale getallen en bitgewijze rekenkundige:

[Flags]
public enum DaysOfWeek
{
    Monday    = 0b00000001,
    Tuesday   = 0b00000010,
    Wednesday = 0b00000100,
    Thursday  = 0b00001000,
    Friday    = 0b00010000,
    Saturday  = 0b00100000,
    Sunday    = 0b01000000,

    Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday,
    Weekends = Saturday | Sunday
}

Cijfer scheidingstekens

Het onderstrepingsteken _ kan worden gebruikt als cijferscheidingsteken. Het kunnen groeperen van cijfers in grote numerieke letterlijke letters heeft een aanzienlijke invloed op de leesbaarheid.

Het onderstrepingsteken kan overal in een numerieke letter voorkomen, behalve zoals hieronder aangegeven. Verschillende groepen kunnen zinvol zijn in verschillende scenario's of met verschillende numerieke basissen.

Elke reeks cijfers kan worden gescheiden door een of meer onderstrepingstekens. De _ is toegestaan in decimalen en in exponenten. De scheidingstekens hebben geen semantische impact - ze worden gewoon genegeerd.

int bin = 0b1001_1010_0001_0100;
int hex = 0x1b_a0_44_fe;
int dec = 33_554_432;
int weird = 1_2__3___4____5_____6______7_______8________9;
double real = 1_000.111_1e-1_000;

Waar het scheidingsteken _ niet mag worden gebruikt:

Taalondersteuning voor Tuples

Basics

Een tuple is een geordende, eindige lijst met elementen. Tuples worden gewoonlijk gebruikt bij het programmeren als een middel om samen met één enkele entiteit te werken in plaats van individueel met elk van de elementen van de tuple te werken, en om individuele rijen (dwz "records") in een relationele database weer te geven.

In C # 7.0 kunnen methoden meerdere retourwaarden hebben. Achter de schermen gebruikt de compiler de nieuwe ValueTuple- structuur.

public (int sum, int count) GetTallies() 
{
    return (1, 2);
}

Kanttekening: om dit te laten werken in Visual Studio 2017, moet u het krijgen System.ValueTuple pakket.

Als een resultaat met tuple-terugkerende methode is toegewezen aan een enkele variabele, hebt u toegang tot de leden met hun gedefinieerde namen op de handtekening van de methode:

var result = GetTallies();
// > result.sum
// 1
// > result.count
// 2

Tuple-deconstructie

Tuple-deconstructie scheidt een tuple in zijn delen.

Als u bijvoorbeeld GetTallies en de retourwaarde toewijst aan twee afzonderlijke variabelen, wordt de tuple gedeconstrueerd in die twee variabelen:

(int tallyOne, int tallyTwo) = GetTallies();

var werkt ook:

(var s, var c) = GetTallies();

Je kunt ook een kortere syntaxis gebruiken, met var buiten () :

var (s, c) = GetTallies();

Je kunt ook deconstrueren in bestaande variabelen:

int s, c;
(s, c) = GetTallies();

Ruilen is nu veel eenvoudiger (geen tijdelijke variabele vereist):

(b, a) = (a, b);

Interessant is dat elk object kan worden gedeconstrueerd door een Deconstruct methode in de klasse te definiëren:

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public void Deconstruct(out string firstName, out string lastName)
    {
        firstName = FirstName;
        lastName = LastName;
    }
}

var person = new Person { FirstName = "John", LastName = "Smith" };
var (localFirstName, localLastName) = person;

In dit geval (localFirstName, localLastName) = person syntaxis van (localFirstName, localLastName) = person Deconstruct op de person .

Deconstructie kan zelfs worden gedefinieerd in een uitbreidingsmethode. Dit komt overeen met het bovenstaande:

public static class PersonExtensions
{
    public static void Deconstruct(this Person person, out string firstName, out string lastName)
    {
        firstName = person.FirstName;
        lastName = person.LastName;
    }
}

var (localFirstName, localLastName) = person;

Een alternatieve benadering voor de klasse Person is om de Name zelf te definiëren als een Tuple . Stel je de volgende situatie voor:

class Person
{
    public (string First, string Last) Name { get; }

    public Person((string FirstName, string LastName) name)
    {
        Name = name;
    }
}

Dan kun je een persoon als volgt instantiëren (waar we een tuple als argument kunnen nemen):

var person = new Person(("Jane", "Smith"));

var firstName = person.Name.First; // "Jane"
var lastName = person.Name.Last;   // "Smith"

Tuple-initialisatie

Je kunt ook willekeurig tupels in code maken:

var name = ("John", "Smith");
Console.WriteLine(name.Item1);
// Outputs John

Console.WriteLine(name.Item2);
// Outputs Smith

Bij het maken van een tuple, kunt u ad-hoc itemnamen toewijzen aan de leden van de tuple:

var name = (first: "John", middle: "Q", last: "Smith");
Console.WriteLine(name.first);
// Outputs John

Type inferentie

Meerdere tupels gedefinieerd met dezelfde handtekening (matching types en count) zullen worden afgeleid als matching types. Bijvoorbeeld:

public (int sum, double average) Measure(List<int> items)
{
    var stats = (sum: 0, average: 0d);
    stats.sum = items.Sum();
    stats.average = items.Average();
    return stats;
}

stats kunnen worden geretourneerd omdat de verklaring van de stats en de retoursignatuur van de methode overeenkomen.

Reflectie en Tuple veldnamen

Ledennamen bestaan niet tijdens runtime. Reflectie beschouwt tuples met hetzelfde aantal en hetzelfde soort leden, zelfs als de namen van de leden niet overeenkomen. Het converteren van een tuple naar een object en vervolgens naar een tuple met dezelfde lidtypen, maar met verschillende namen, zal ook geen uitzondering veroorzaken.

Hoewel de ValueTuple-klasse zelf geen informatie voor ledennamen bewaart, is de informatie beschikbaar via reflectie in een TupleElementNamesAttribute. Dit kenmerk wordt niet toegepast op de tuple zelf, maar op methodeparameters, retourwaarden, eigenschappen en velden. Dit maakt het mogelijk om tuple itemnamen te behouden in assemblies, dwz als een methode retourneert (stringnaam, int count), zullen de naamnaam en het aantal beschikbaar zijn voor aanroepen van de methode in een andere assembly omdat de retourwaarde wordt gemarkeerd met TupleElementNameAttribute dat de waarden bevat "name" en "count".

Gebruik met generieke geneesmiddelen en async

De nieuwe tuple-functies (met behulp van het onderliggende ValueTuple type) ondersteunen generieke geneesmiddelen volledig en kunnen worden gebruikt als generieke parameter. Dat maakt het mogelijk om ze te gebruiken met het async / await patroon:

public async Task<(string value, int count)> GetValueAsync()
{
    string fooBar = await _stackoverflow.GetStringAsync();
    int num = await _stackoverflow.GetIntAsync();

    return (fooBar, num);
}

Gebruik met collecties

Het kan handig zijn om een verzameling tupels te hebben in (een voorbeeld) een scenario waarin u probeert een overeenkomende tupel te vinden op basis van voorwaarden om vertakking van de code te voorkomen.

Voorbeeld:

private readonly List<Tuple<string, string, string>> labels = new List<Tuple<string, string, string>>()
{
    new Tuple<string, string, string>("test1", "test2", "Value"),
    new Tuple<string, string, string>("test1", "test1", "Value2"),
    new Tuple<string, string, string>("test2", "test2", "Value3"),
};

public string FindMatchingValue(string firstElement, string secondElement)
{
    var result = labels
        .Where(w => w.Item1 == firstElement && w.Item2 == secondElement)
        .FirstOrDefault();

    if (result == null)
        throw new ArgumentException("combo not found");

    return result.Item3;
}

Met de nieuwe tupels kan worden:

private readonly List<(string firstThingy, string secondThingyLabel, string foundValue)> labels = new List<(string firstThingy, string secondThingyLabel, string foundValue)>()
{
    ("test1", "test2", "Value"),
    ("test1", "test1", "Value2"),
    ("test2", "test2", "Value3"),
}

public string FindMatchingValue(string firstElement, string secondElement)
{
    var result = labels
        .Where(w => w.firstThingy == firstElement && w.secondThingyLabel == secondElement)
        .FirstOrDefault();

    if (result == null)
        throw new ArgumentException("combo not found");

    return result.foundValue;
}

Hoewel de naamgeving in het bovenstaande voorbeeld tuple vrij generiek is, zorgt het idee van relevante labels voor een beter begrip van wat er wordt geprobeerd in de code boven "item1", "item2" en "item3".

Verschillen tussen ValueTuple en Tuple

De belangrijkste reden voor de introductie van ValueTuple is prestaties.

Typ de naam ValueTuple Tuple
Klasse of structuur struct class
Mutabiliteit (waarden veranderen na creatie) veranderlijk onveranderlijk
Leden benoemen en ondersteuning voor andere talen Ja nee ( TBD )

Referenties

Lokale functies

Lokale functies worden binnen een methode gedefinieerd en zijn daarbuiten niet beschikbaar. Ze hebben toegang tot alle lokale variabelen en ondersteunen iterators, async / await en lambda-syntaxis. Op deze manier kunnen herhalingen die specifiek zijn voor een functie worden gefunctionaliseerd zonder de klas te verdringen. Als bijwerking verbetert dit de prestaties van de suggestie van intellisense.

Voorbeeld

double GetCylinderVolume(double radius, double height)
{
    return getVolume();

    double getVolume()
    {
        // You can declare inner-local functions in a local function 
        double GetCircleArea(double r) => Math.PI * r * r;

        // ALL parents' variables are accessible even though parent doesn't have any input. 
        return GetCircleArea(radius) * height;
    }
}

Lokale functies vereenvoudigen de code aanzienlijk voor LINQ-operatoren, waar u meestal argumentcontroles moet scheiden van de werkelijke logica om argumentcontroles direct te maken, niet vertraagd tot nadat de iteratie is gestart.

Voorbeeld

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source, 
    Func<TSource, bool> predicate)
{
    if (source == null) throw new ArgumentNullException(nameof(source));
    if (predicate == null) throw new ArgumentNullException(nameof(predicate));

    return iterator();

    IEnumerable<TSource> iterator()
    {
        foreach (TSource element in source)
            if (predicate(element))
                yield return element;
    }
}

Lokale functies ondersteunen ook de async en await trefwoorden.

Voorbeeld

async Task WriteEmailsAsync()
{
    var emailRegex = new Regex(@"(?i)[a-z0-9_.+-]+@[a-z0-9-]+\.[a-z0-9-.]+");
    IEnumerable<string> emails1 = await getEmailsFromFileAsync("input1.txt");
    IEnumerable<string> emails2 = await getEmailsFromFileAsync("input2.txt");
    await writeLinesToFileAsync(emails1.Concat(emails2), "output.txt");

    async Task<IEnumerable<string>> getEmailsFromFileAsync(string fileName)
    {
        string text;

        using (StreamReader reader = File.OpenText(fileName))
        {
            text = await reader.ReadToEndAsync();
        }

        return from Match emailMatch in emailRegex.Matches(text) select emailMatch.Value;
    }

    async Task writeLinesToFileAsync(IEnumerable<string> lines, string fileName)
    {
        using (StreamWriter writer = File.CreateText(fileName))
        {
            foreach (string line in lines)
            {
                await writer.WriteLineAsync(line);
            }
        }
    }
}

Een belangrijk ding dat u misschien is opgevallen, is dat lokale functies kunnen worden gedefinieerd onder de return instructie, ze hoeven er niet boven te worden gedefinieerd. Bovendien volgen lokale functies doorgaans de naamgevingsconventie "lowerCamelCase" om zich gemakkelijker te onderscheiden van functies van klassebereik.

Patroon matching

Patroonaanpassingsuitbreidingen voor C # maken veel van de voordelen van patroonafstemming uit functionele talen mogelijk, maar op een manier die soepel integreert met het gevoel van de onderliggende taal

switch uitdrukking

Patroonaanpassing breidt de switch om typen in te schakelen:

class Geometry {} 

class Triangle : Geometry
{
    public int Width { get; set; }
    public int Height { get; set; }
    public int Base { get; set; }
}

class Rectangle : Geometry
{
    public int Width { get; set; }
    public int Height { get; set; }
}

class Square : Geometry
{
    public int Width { get; set; }
}

public static void PatternMatching()
{
    Geometry g = new Square { Width = 5 }; 
    
    switch (g)
    {
        case Triangle t:
            Console.WriteLine($"{t.Width} {t.Height} {t.Base}");
            break;
        case Rectangle sq when sq.Width == sq.Height:
            Console.WriteLine($"Square rectangle: {sq.Width} {sq.Height}");
            break;
        case Rectangle r:
            Console.WriteLine($"{r.Width} {r.Height}");
            break;
        case Square s:
            Console.WriteLine($"{s.Width}");
            break;
        default:
            Console.WriteLine("<other>");
            break;
    }
}

is uitdrukking

Patroonherkenning breidt de is operator te controleren op het type en verklaren een nieuwe variabele tegelijkertijd.

Voorbeeld

7.0
string s = o as string;
if(s != null)
{
    // do something with s
}

kan worden herschreven als:

7.0
if(o is string s)
{
    //Do something with s
};

Merk ook op dat het bereik van de patroonvariabele s wordt uitgebreid tot buiten het if blok dat het einde van het omsluitende bereik bereikt, bijvoorbeeld:

if(someCondition)
{
   if(o is string s)
   {
      //Do something with s
   }
   else
   {
     // s is unassigned here, but accessible 
   }

   // s is unassigned here, but accessible 
}
// s is not accessible here

ref retour en ref lokaal

Ref-retouren en ref-locals zijn handig voor het manipuleren en retourneren van verwijzingen naar geheugenblokken in plaats van geheugen te kopiëren zonder toevlucht te nemen tot onveilige verwijzingen.

Ref Return

public static ref TValue Choose<TValue>(
    Func<bool> condition, ref TValue left, ref TValue right)
{
    return condition() ? ref left : ref right;
}

Hiermee kunt u twee waarden doorgeven door middel van referentie, waarvan er één wordt geretourneerd op basis van een voorwaarde:

Matrix3D left = …, right = …;
Choose(chooser, ref left, ref right).M20 = 1.0;

Ref Lokaal

public static ref int Max(ref int first, ref int second, ref int third)
{
    ref int max = first > second ? ref first : ref second;
    return max > third ? ref max : ref third;
}
…
int a = 1, b = 2, c = 3;
Max(ref a, ref b, ref c) = 4;
Debug.Assert(a == 1); // true
Debug.Assert(b == 2); // true
Debug.Assert(c == 4); // true

Onveilige ref-operaties

In System.Runtime.CompilerServices.Unsafe een set onveilige bewerkingen gedefinieerd waarmee u ref waarden kunt manipuleren alsof het in feite pointers waren.

Bijvoorbeeld, een geheugenadres ( ref ) opnieuw interpreteren als een ander type:

byte[] b = new byte[4] { 0x42, 0x42, 0x42, 0x42 };

ref int r = ref Unsafe.As<byte, int>(ref b[0]);
Assert.Equal(0x42424242, r);

0x0EF00EF0;
Assert.Equal(0xFE, b[0] | b[1] | b[2] | b[3]);

Let echter op endianness wanneer u dit doet, bijv. Controleer BitConverter.IsLittleEndian indien nodig en BitConverter.IsLittleEndian dienovereenkomstig.

Of doorzoek een array op een onveilige manier:

int[] a = new int[] { 0x123, 0x234, 0x345, 0x456 };

ref int r1 = ref Unsafe.Add(ref a[0], 1);
Assert.Equal(0x234, r1);

ref int r2 = ref Unsafe.Add(ref r1, 2);
Assert.Equal(0x456, r2);

ref int r3 = ref Unsafe.Add(ref r2, -3);
Assert.Equal(0x123, r3);

Of de soortgelijke Subtract :

string[] a = new string[] { "abc", "def", "ghi", "jkl" };

ref string r1 = ref Unsafe.Subtract(ref a[0], -2);
Assert.Equal("ghi", r1);

ref string r2 = ref Unsafe.Subtract(ref r1, -1);
Assert.Equal("jkl", r2);

ref string r3 = ref Unsafe.Subtract(ref r2, 3);
Assert.Equal("abc", r3);

Bovendien kan worden gecontroleerd of twee ref waarden hetzelfde zijn, dwz hetzelfde adres:

long[] a = new long[2];

Assert.True(Unsafe.AreSame(ref a[0], ref a[0]));
Assert.False(Unsafe.AreSame(ref a[0], ref a[1]));

Roslyn Github Issue

System.Runtime.CompilerServices.Unsafe op github

gooi uitdrukkingen

Met C # 7.0 kan op bepaalde plaatsen worden gegooid:

class Person
{
    public string Name { get; }

    public Person(string name) => Name = name ?? throw new ArgumentNullException(nameof(name));

    public string GetFirstName()
    {
        var parts = Name.Split(' ');
        return (parts.Length > 0) ? parts[0] : throw new InvalidOperationException("No name!");
    }

    public string GetLastName() => throw new NotImplementedException();
}

Voorafgaand aan C # 7.0, als u een uitzondering van een expressie-instantie wilde weggooien, zou u:

var spoons = "dinner,desert,soup".Split(',');

var spoonsArray = spoons.Length > 0 ? spoons : null;

if (spoonsArray == null) 
{
    throw new Exception("There are no spoons");
}

Of

var spoonsArray = spoons.Length > 0 
    ? spoons 
    : new Func<string[]>(() => 
      {
          throw new Exception("There are no spoons");
      })();

In C # 7.0 is het bovenstaande nu vereenvoudigd tot:

var spoonsArray = spoons.Length > 0 ? spoons : throw new Exception("There are no spoons");

Uitgebreide uitgebreide ledenlijst met expressie

C # 7.0 voegt accessors, constructors en finalizers toe aan de lijst met dingen die expressie-instanties kunnen hebben:

class Person
{
    private static ConcurrentDictionary<int, string> names = new ConcurrentDictionary<int, string>();

    private int id = GetId();

    public Person(string name) => names.TryAdd(id, name); // constructors

    ~Person() => names.TryRemove(id, out _);              // finalizers

    public string Name
    {
        get => names[id];                                 // getters
        set => names[id] = value;                         // setters
    }
}

Zie ook het gedeelte over de var-aangifte voor de verwijderingsoperator.

ValueTask

Task<T> is een klasse en veroorzaakt de onnodige overhead van de toewijzing wanneer het resultaat onmiddellijk beschikbaar is.

ValueTask<T> is een structuur en is ingevoerd om de toewijzing van een voorkomen Task object bij het resultaat van de asynchrone werking reeds op het tijdstip van wachten.

Dus ValueTask<T> biedt twee voordelen:

1. Prestatieverbetering

Hier is een voorbeeld van een Task<T> :

  • Vereist heap-toewijzing
  • Duurt 120ns met JIT
async Task<int> TestTask(int d)
{
    await Task.Delay(d);
    return 10;
}

Hier is het analoge ValueTask<T> :

  • Geen heap-toewijzing als het resultaat synchroon bekend is (wat in dit geval niet het geval is vanwege de Task.Delay , maar vaak in veel real-world async / await scenario's)
  • Duurt 65ns met JIT
async ValueTask<int> TestValueTask(int d)
{
    await Task.Delay(d);
    return 10;
}

2. Verhoogde implementatieflexibiliteit

Implementaties van een async-interface die synchroon wenst te zijn, zouden anders gedwongen zijn Task.Run of Task.FromResult (resulterend in de hierboven beschreven prestatieboete). Er is dus enige druk tegen synchrone implementaties.

Maar met ValueTask<T> zijn implementaties ValueTask<T> om te kiezen tussen synchroon of asynchroon zonder bellers te beïnvloeden.

Hier is bijvoorbeeld een interface met een asynchrone methode:

interface IFoo<T>
{
    ValueTask<T> BarAsync();
}

... en hier is hoe die methode zou kunnen worden genoemd:

IFoo<T> thing = getThing();
var x = await thing.BarAsync();

Met ValueTask werkt de bovenstaande code met synchrone of asynchrone implementaties :

Synchrone implementatie:

class SynchronousFoo<T> : IFoo<T>
{
    public ValueTask<T> BarAsync()
    {
        var value = default(T);
        return new ValueTask<T>(value);
    }
}

Asynchrone implementatie

class AsynchronousFoo<T> : IFoo<T>
{
    public async ValueTask<T> BarAsync()
    {
        var value = default(T);
        await Task.Delay(1);
        return value;
    }
}

Notes

Hoewel ValueTask struct was gepland om te worden toegevoegd aan C # 7.0 , is het voorlopig als een andere bibliotheek bewaard. ValueTask <T> System.Threading.Tasks.Extensions pakket kan worden gedownload van Nuget Gallery



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow