Assinaturas de método mágico em C #

Apresento a você a tradução de The Magical Methods in C # de CEZARY PIĄTEK .



Há um conjunto específico de assinaturas de método em C # que possuem suporte ao idioma. Métodos com essas assinaturas permitem o uso de sintaxe especial com todas as suas vantagens. Por exemplo, eles podem ser usados ​​para simplificar nosso código ou criar uma DSL para expressar uma solução para um problema de uma maneira mais bonita. Como vejo esses métodos em todo o lugar, decidi escrever um post e resumir todas as minhas descobertas sobre esse tópico, a saber:



  • Sintaxe de inicialização da coleção
  • Sintaxe de inicialização do dicionário
  • Desconstrutores
  • Tipos aguardáveis ​​personalizados
  • O padrão de expressão de consulta


Sintaxe de inicialização da coleção



A sintaxe de inicialização da coleção é bastante antiga, pois existe desde o C # 3.0 (lançado no final de 2007). Deixe-me lembrá-lo de que a sintaxe de inicialização da coleção permite criar uma lista com elementos em um bloco:



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


Este código é equivalente ao seguinte:



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.





O objetivo deste artigo não é convencê-lo a abusar desses truques sintáticos, mas torná-los mais compreensíveis. Por outro lado, nem sempre podem ser evitados. Eles foram projetados para serem usados ​​e, às vezes, podem melhorar seu código. Se você tem medo de que o código resultante seja incompreensível para seus colegas, você precisa encontrar uma maneira de compartilhar conhecimento com eles (ou pelo menos um link para este artigo). Não tenho certeza de que este seja um conjunto completo desses "métodos mágicos". Portanto, se você souber mais, compartilhe nos comentários.




All Articles