Método mágico de firmas en C #

Les presento la traducción de Los métodos mágicos en C # por CEZARY PIĄTEK .



Hay un conjunto específico de firmas de métodos en C # que tienen soporte de nivel de lenguaje. Los métodos con tales firmas le permiten usar una sintaxis personalizada con todas sus ventajas. Por ejemplo, se pueden usar para simplificar nuestro código o crear un DSL para expresar una solución a un problema de una manera más bella. Veo tales métodos por todas partes, así que decidí escribir una publicación y resumir todos mis hallazgos sobre este tema, a saber:



  • Sintaxis de inicialización de colección
  • Sintaxis de inicialización del diccionario
  • Deconstructores
  • Tipos personalizados de espera
  • El patrón de expresión de consulta


Sintaxis de inicialización de colección



La sintaxis de inicialización de la colección es una característica bastante antigua, porque existe con C # 3.0 (lanzado a finales de 2007). Permítame recordarle que la sintaxis de inicialización de la colección le permite crear una lista con elementos en un bloque:



var list = new List<int> { 1, 2, 3 };


Este código es equivalente al siguiente:



var temp = new List<int>();
temp.Add(1);
temp.Add(2);
temp.Add(3);
var list = temp;


BCL. , :



  • IEnumerable
  • void Add(T item)


public class CustomList<T>: IEnumerable
{
    public IEnumerator GetEnumerator() => throw new NotImplementedException();
    public void Add(T item) => throw new NotImplementedException();
}


, Add :



public static class ExistingTypeExtensions
{
    public static void Add<T>(this ExistingType @this, T item) => throw new NotImplementedException();
}


- :



class CustomType
{
    public List<string> CollectionField { get; private set; }  = new List<string>();
}

class Program
{
    static void Main(string[] args)
    {
        var obj = new CustomType
        {
            CollectionField =
            {
                "item1",
                "item2"
            }
        };
    }
}


. ? :



var obj = new CustomType
{
    CollectionField =
    {
        { existingItems }
    }
};


, :



  • IEnumerable
  • void Add(IEnumerable<T> items)


public class CustomList<T>: IEnumerable
{
    public IEnumerator GetEnumerator() => throw new NotImplementedException();
    public void Add(IEnumerable<T> items) => throw new NotImplementedException();
}


, BCL void Add(IEnumerable<T> items), , :



public static class ListExtensions
{
    public static void Add<T>(this List<T> @this, IEnumerable<T> items) => @this.AddRange(items);
}


:



var obj = new CustomType
{
    CollectionField =
    {
        { existingItems.Where(x => /*Filter items*/).Select(x => /*Map items*/) }
    }
};


(IEnumerable):



var obj = new CustomType
{
    CollectionField =
    {
        individualElement1,
        individualElement2,
        { list1.Where(x => /*Filter items*/).Select(x => /*Map items*/) },
        { list2.Where(x => /*Filter items*/).Select(x => /*Map items*/) },
    }
};


.



, -, protobuf. , protobuf: grpctools .NET proto, - :



[DebuggerNonUserCode]
public RepeatableField<ItemType> SomeCollectionField
{
    get
    {
        return this.someCollectionField_;
    }
}


, - , RepeatableField void Add(IEnumerable items), - :



/// <summary>
/// Adds all of the specified values into this collection. This method is present to
/// allow repeated fields to be constructed from queries within collection initializers.
/// Within non-collection-initializer code, consider using the equivalent <see cref="AddRange"/>
/// method instead for clarity.
/// </summary>
/// <param name="values">The values to add to this collection.</param>
public void Add(IEnumerable<T> values)
{
    AddRange(values);
}




C# 6.0 — , . :



var errorCodes = new Dictionary<int, string>
{
    [404] = "Page not Found",
    [302] = "Page moved, but left a forwarding address.",
    [500] = "The web server can't come out to play today."
};


:



var errorCodes = new Dictionary<int, string>();
errorCodes[404] = "Page not Found";
errorCodes[302] = "Page moved, but left a forwarding address.";
errorCodes[500] = "The web server can't come out to play today.";


, .



— , Dictionary<T> , :



class HttpHeaders
{
    public string this[string key]
    {
        get => throw new NotImplementedException();
        set => throw new NotImplementedException();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var headers = new HttpHeaders
        {
            ["access-control-allow-origin"] = "*",
            ["cache-control"] = "max-age=315360000, public, immutable"
        };
    }
}




C# 7.0 . :



var point = (5, 7);
// decomposing tuple into separated variables
var (x, y) = point;


:



ValueTuple<int, int> point = new ValueTuple<int, int>(1, 4);
int x = point.Item1;
int y = point.Item2;


:



int x = 5, y = 7;
//switch
(x, y) = (y,x);


:



class Point
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y)  => (X, Y) = (x, y);
}


, . , :



  • Deconstruct
  • void
  • out


Point :



class Point
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y) => (X, Y) = (x, y);

    public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}


:



var point = new Point(2, 4);
var (x, y) = point;


" " :



int x;
int y;
new Point(2, 4).Deconstruct(out x, out y);


:



public static class PointExtensions
{
     public static void Deconstruct(this Point @this, out int x, out int y) => (x, y) = (@this.X, @this.Y);
}


KeyValuePair<TKey, TValue>, :



foreach (var (key, value) in new Dictionary<int, string> { [1] = "val1", [2] = "val2" })
{
    //TODO: Do something
}


KeyValuePair<TKey, TValue>.Deconstruct(TKey, TValue) netstandard2.1. netstandard .



awaitable



C# 5.0 ( Visual Studio 2012) async/await, . , :



void DoSomething()
{
    DoSomethingAsync().ContinueWith((task1) => {
        if (task1.IsCompletedSuccessfully)
        {
            DoSomethingElse1Async(task1.Result).ContinueWith((task2) => {
                if (task2.IsCompletedSuccessfully)
                {
                    DoSomethingElse2Async(task2.Result).ContinueWith((task3) => {
                        //TODO: Do something
                    });
                }
            });
        }
    });
}

private Task<int> DoSomethingAsync() => throw new NotImplementedException();
private Task<int> DoSomethingElse1Async(int i) => throw new NotImplementedException();
private Task<int> DoSomethingElse2Async(int i) => throw new NotImplementedException();


async/await:



async Task DoSomething()
{
    var res1 = await DoSomethingAsync();
    var res2 = await DoSomethingElse1Async(res1);
    await DoSomethingElse2Async(res2);
}


, await Task. , GetAwaiter, :



  • System.Runtime.CompilerServices.INotifyCompletion void OnCompleted(Action continuation)
  • IsCompleted
  • GetResult


await GetAwaiter, TaskAwaiter<TResult> , :



class CustomAwaitable
{
    public CustomAwaiter GetAwaiter() => throw new NotImplementedException();
}

class CustomAwaiter: INotifyCompletion
{
    public void OnCompleted(Action continuation) => throw new NotImplementedException();

    public bool IsCompleted => throw new NotImplementedException();

    public void GetResult() => throw new NotImplementedException();
}


: " await awaitable ?". , Stephen Toub "await anything", .



query expression



C# 3.0 — Language-Integrated Query, LINQ, SQL- . LINQ : SQL- . , . . , . LINQ , SQL- , . . C#, CLR. LINQ IEnumerable, IEnumerable<T> IQuerable<T>, , , query expression. , LINQ, :



class C
{
    public C<T> Cast<T>();
}

class C<T> : C
{
    public C<T> Where(Func<T,bool> predicate);

    public C<U> Select<U>(Func<T,U> selector);

    public C<V> SelectMany<U,V>(Func<T,C<U>> selector, Func<T,U,V> resultSelector);

    public C<V> Join<U,K,V>(C<U> inner, Func<T,K> outerKeySelector, Func<U,K> innerKeySelector, Func<T,U,V> resultSelector);

    public C<V> GroupJoin<U,K,V>(C<U> inner, Func<T,K> outerKeySelector, Func<U,K> innerKeySelector, Func<T,C<U>,V> resultSelector);

    public O<T> OrderBy<K>(Func<T,K> keySelector);

    public O<T> OrderByDescending<K>(Func<T,K> keySelector);

    public C<G<K,T>> GroupBy<K>(Func<T,K> keySelector);

    public C<G<K,E>> GroupBy<K,E>(Func<T,K> keySelector, Func<T,E> elementSelector);
}

class O<T> : C<T>
{
    public O<T> ThenBy<K>(Func<T,K> keySelector);

    public O<T> ThenByDescending<K>(Func<T,K> keySelector);
}

class G<K,T> : C<T>
{
    public K Key { get; }
}


, , LINQ . LINQ . , , Understand monads with LINQ Miłosz Piechocki.





El propósito de este artículo no es convencerte de abusar de estos trucos sintácticos, sino aclararlos. Por otro lado, no siempre se pueden evitar. Fueron diseñados para ser utilizados, y a veces pueden mejorar su código. Si tiene miedo de que el código resultante sea incomprensible para sus colegas, debe encontrar una manera de compartir sus conocimientos con ellos (o al menos un enlace a este artículo). No estoy seguro de que este sea un conjunto completo de tales "métodos mágicos", así que si sabes algo más, comparte los comentarios.




All Articles