Prefacio #
Hay una cosa que se llama tipo de pato, el llamado duck typing es que mientras las cosas se comportan como un pato a continuación, ser capaz de lanzar esta cosa es un pato.
C # es en realidad oculta dentro de una gran cantidad de tipos similares de patos, pero muchos desarrolladores no conocen y, por lo tanto, no puede hacer uso de estas cosas, así que hoy estoy escondido en el desglose de lo que estos compiladores en detalle.
No solo Task
y ValueTask
al await
#
Al escribir un código asíncrono en C #, a menudo elegimos un código asíncrono está contenida en uno Task
o ValueTask
, la persona que llama será capaz de utilizar esta await
forma de lograr una llamada asincrónica.
Xika Xi, no es la única Task
y ValueTask
la capacidad await
. Task
Y ValueTask
detrás del grupo de subprocesos es, obviamente, involucrado en la programación, pero ¿por qué el C # async
/ await
se dice que es coroutine
él?
Debido a que await
no son necesariamente cosas Task
/ ValueTask
, en C # siempre y cuando su clase contiene GetAwaiter()
métodos y bool IsCompleted
propiedades, y GetAwaiter()
las cosas volvieron contiene un GetResult()
método, una bool IsCompleted
propiedad e implementado INotifyCompletion
, el objeto de esta clase es que pueda await
de.
Por lo tanto, en las operaciones de paquete de E / S, podemos lograr un auto Awaiter
, que se basa en el subyacente epoll
/ IOCP
aplicación, de manera que cuando await
el tiempo no crea ningún hilo, también sin ningún tipo de programación de subprocesos, pero a directamente el control. El sistema operativo después de la finalización de E / S llamando al CompletionPort
(Windows), etc., para informar al modo de usuario para completar una llamada asincrónica, a continuación, reanudar el contexto continúan con la lógica restante, que es en realidad una verdadera stackless coroutine
.
public class MyTask<T>
{
public MyAwaiter<T> GetAwaiter()
{
return new MyAwaiter<T>();
}
}
public class MyAwaiter<T> : INotifyCompletion
{
public bool IsCompleted { get; private set; }
public T GetResult()
{
throw new NotImplementedException();
}
public void OnCompleted(Action continuation)
{
throw new NotImplementedException();
}
}
public class Program
{
static async Task Main(string[] args)
{
var obj = new MyTask<int>();
await obj;
}
}
De hecho, el .NET Core E / S asociada con la API asíncrona está haciendo de hecho es así, el proceso de operación de E / S no tendrá ningún hilo de la espera de los resultados de la asignación están coroutine
funcionando: las operaciones de E / S directa comienzan dejar fuera de control hasta que la operación de E / S se ha completado. La razón por la que a veces se encuentra await
antes y después de que el mensaje ha cambiado, es sólo porque Task
sí está previsto.
UWP utilizado en el desarrollo de IAsyncAction
/ IAsyncOperation<T>
es de la parte inferior del envase, y Task
no tiene nada que hacer, pero lata await
, y si el C ++ / WinRT uwp desarrollo luego regresar estas interfaces son también posibles métodos co_await
de.
No solo IEnumerable
y IEnumerator
con el fin de ser foreach
#
menudo vamos a escribir el siguiente código:
foreach (var i in list)
{
// ......
}
Entonces uno puede preguntarse por qué foreach
, porque la mayoría de ellos responderá que list
los implementos IEnumerable
o IEnumerator
.
Pero, de hecho, si usted quiere ser un objeto foreach
, sólo se necesita proporcionar un GetEnumerator()
método y el GetEnumerator()
objeto devuelto contiene una bool MoveNext()
añadir un método Current
atributo.
class MyEnumerator<T>
{
public T Current { get; private set; }
public bool MoveNext()
{
throw new NotImplementedException();
}
}
class MyEnumerable<T>
{
public MyEnumerator<T> GetEnumerator()
{
throw new NotImplementedException();
}
}
class Program
{
public static void Main()
{
var x = new MyEnumerable<int>();
foreach (var i in x)
{
// ......
}
}
}
No solo IAsyncEnumerable
y IAsyncEnumerator
con el fin de ser await foreach
#
Como el anterior, pero esta vez los requisitos han cambiado, GetEnumerator()
y MoveNext()
cambiado GetAsyncEnumerator()
y MoveNextAsync()
.
En el que MoveNextAsync()
las cosas deberían ser un retorno Awaitable<bool>
, ya que esto Awaitable
lo que al final es que puede ser Task
/ ValueTask
puede ser otra o en la propia aplicación.
class MyAsyncEnumerator<T>
{
public T Current { get; private set; }
public MyTask<bool> MoveNextAsync()
{
throw new NotImplementedException();
}
}
class MyAsyncEnumerable<T>
{
public MyAsyncEnumerator<T> GetAsyncEnumerator()
{
throw new NotImplementedException();
}
}
class Program
{
public static async Task Main()
{
var x = new MyAsyncEnumerable<int>();
await foreach (var i in x)
{
// ......
}
}
}
ref struct
¿Cómo lograr IDisposable
#
Como todos sabemos ref struct
, ya que es necesario y no se pueden encajonar en la pila, no se puede implementar la interfaz, pero si usted ref struct
tiene una void Dispose()
para que pueda ser utilizado using
para lograr la sintaxis objeto se destruye automáticamente.
ref struct MyDisposable
{
public void Dispose() => throw new NotImplementedException();
}
class Program
{
public static void Main()
{
using var y = new MyDisposable();
// ......
}
}
No sólo Range
se puede utilizar rebanadas #
C # 8 introduce Ranges, permitiendo que la operación de corte, pero en realidad no es necesario proporcionar un receptor Range
indexador puede escribir parámetros para utilizar esta característica.
Mientras que su clase puede ser contado (propiedad Length
o Count
atributos), y puede ser en rodajas (para tener un Slice(int, int)
método), a continuación, puede utilizar esta función.
class MyRange
{
public int Count { get; private set; }
public object Slice(int x, int y) => throw new NotImplementedException();
}
class Program
{
public static void Main()
{
var x = new MyRange();
var y = x[1..];
}
}
No sólo Index
se puede utilizar el índice #
C # 8 índices para la introducción del índice, tales como el uso ^1
del elemento de índice penúltima, pero en realidad no es necesario proporcionar un recibir Index
indexador puede escribir parámetros para utilizar esta característica.
Mientras que su clase puede ser contado (propiedad Length
o Count
atributos) y puede ser indexado (tiene un receptor int
parámetros indexador), a continuación, puede utilizar esta función.
class MyIndex
{
public int Count { get; private set; }
public object this[int index]
{
get => throw new NotImplementedException();
}
}
class Program
{
public static void Main()
{
var x = new MyIndex();
var y = x[^1];
}
}
Para lograr el tipo de deconstrucción #
¿Cómo lograr un tipo de deconstrucción de la misma? De hecho, acaba de escribir un nombre para Deconstruct()
el método y los parámetros son out
puede ser.
class MyDeconstruct
{
private int A => 1;
private int B => 2;
public void Deconstruct(out int a, out int b)
{
a = A;
b = B;
}
}
class Program
{
public static void Main()
{
var x = new MyDeconstruct();
var (o, u) = x;
}
}
No sólo se dio cuenta IEnumerable
con el fin de utilizar LINQ
#
LINQ
C # se utiliza comúnmente en un lenguaje de consulta integrado le permite escribir código como este:
from c in list where c.Id > 5 select c;
Sin embargo, en el código anterior list
tipos no necesariamente tiene que darse cuenta IEnumerable
, de hecho, siempre y cuando no es un nombre correspondiente métodos de extensión puede ser, por ejemplo, ha llamado el Select
método puede ser utilizado select
, con los conocidos Where
métodos se pueden utilizar where
.
class Just<T> : Maybe<T>
{
private readonly T value;
public Just(T value) { this.value = value; }
public override Maybe<U> Select<U>(Func<T, Maybe<U>> f) => f(value);
public override string ToString() => $"Just {value}";
}
class Nothing<T> : Maybe<T>
{
public override Maybe<U> Select<U>(Func<T, Maybe<U>> _) => new Nothing<U>();
public override string ToString() => "Nothing";
}
abstract class Maybe<T>
{
public abstract Maybe<U> Select<U>(Func<T, Maybe<U>> f);
public Maybe<V> SelectMany<U, V>(Func<T, Maybe<U>> k, Func<T, U, V> s)
=> Select(x => k(x).Select(y => new Just<V>(s(x, y))));
public Maybe<U> Where(Func<Maybe<T>, bool> f) => f(this) ? this : new Nothing<T>();
}
class Program
{
public static void Main()
{
var x = new Just<int>(3);
var y = new Just<int>(7);
var z = new Nothing<int>();
var u = from x0 in x from y0 in y select x0 + y0;
var v = from x0 in x from z0 in z select x0 + z0;
var just = from c in x where true select c;
var nothing = from c in x where false select c;
}
}