Async Await Best Practices Cheat Sheet

Summary of Asynchronous Programming Guidelines

Name Description Exceptions
Avoid async void Prefer async Task methods over async void methods Event handlers
Async all the way Don’t mix blocking and async code Console main method
Configure context Use ConfigureAwait(false) when you can Methods that require con­text

The Async Way of Doing Things

To Do This … Instead of This … Use This
Retrieve the result of a background task Task.Wait or Task.Result await
Wait for any task to complete Task.WaitAny await Task.WhenAny
Retrieve the results of multiple tasks Task.WaitAll await Task.WhenAll
Wait a period of time Thread.Sleep await Task.Delay

Know Your Tools

There’s a lot to learn about async and await, and it’s natural to get a little disoriented. Here’s a quick reference of solutions to common problems.

Solutions to Common Async Problems

Problem Solution
Create a task to execute code Task.Run or TaskFactory.StartNew (not the Task constructor or Task.Start)
Create a task wrapper for an operation or event TaskFactory.FromAsync or TaskCompletionSource<T>
Support cancellation CancellationTokenSource and CancellationToken
Report progress IProgress<T> and Progress<T>
Handle streams of data TPL Dataflow or Reactive Extensions
Synchronize access to a shared resource SemaphoreSlim
Asynchronously initialize a resource AsyncLazy<T>
Async-ready producer/consumer structures TPL Dataflow or AsyncCollection<T>

Async and Await Guidelines

There are many new await-friendly techniques that should be used instead of the old blocking techniques. If you have any of these Old examples in your new async code, you’re Doing It Wrong(TM):

Old New Description
task.Wait await task Wait/await for a task to complete
task.Result await task Get the result of a completed task
Task.WaitAny await Task.WhenAny Wait/await for one of a collection of tasks to complete
Task.WaitAll await Task.WhenAll Wait/await for every one of a collection of tasks to complete
Thread.Sleep await Task.Delay Wait/await for a period of time
Taskconstructor Task.Run or TaskFactory.StartNew Create a code-based task

 

C# Yield Usage

You can use a yield return statement to return each element one at a time. It removes the need for an explicit extra class.
You can use a yield break statement to end the iteration.

public class PowersOf2
{
    static void Main()
    {
        // Display powers of 2 up to the exponent of 8:
        foreach (int i in Power(2, 8))
        {
            Console.Write("{0} ", i);
        }
    }

    public static System.Collections.Generic.IEnumerable<int> Power(int number, int exponent)
    {
        int result = 1;

        for (int i = 0; i < exponent; i++)
        {
            result = result * number;
            yield return result;
        }
    }

    // Output: 2 4 8 16 32 64 128 256
}
public static class GalaxyClass
{
    public static void ShowGalaxies()
    {
        var theGalaxies = new Galaxies();
        foreach (Galaxy theGalaxy in theGalaxies.NextGalaxy)
        {
            Debug.WriteLine(theGalaxy.Name + " " + theGalaxy.MegaLightYears.ToString());
        }
    }

    public class Galaxies
    {
        public System.Collections.Generic.IEnumerable<Galaxy> NextGalaxy
        {
            get
            {
                yield return new Galaxy { Name = "Tadpole", MegaLightYears = 400 };
                yield return new Galaxy { Name = "Pinwheel", MegaLightYears = 25 };
                yield return new Galaxy { Name = "Milky Way", MegaLightYears = 0 };
                yield return new Galaxy { Name = "Andromeda", MegaLightYears = 3 };
            }
        }
    }

    public class Galaxy
    {
        public String Name { get; set; }
        public int MegaLightYears { get; set; }
    }
}

Getting And Setting Property Attribute Value By Property Name

You can change property attribute value of an existing attribute at runtime. Below I created a helper class for getting and setting BrowsableAttribute value.

    public static class AttributeHelper
    {
        public static bool GetBrowsable(Type type, string propertyName)
        {
            PropertyDescriptor pd = TypeDescriptor.GetProperties(type)[propertyName];

            if (pd == null)
            {
                return false;
            }
            else
            {
                BrowsableAttribute ba = (BrowsableAttribute)pd.Attributes[typeof(BrowsableAttribute)];
                return (bool)ba.GetType().GetField("browsable", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(ba);
            }
        }

        public static void SetBrowsable(Type type, string propertyName, bool browsable)
        {
            PropertyDescriptor pd = TypeDescriptor.GetProperties(type)[propertyName];

            if (pd != null)
            {
                BrowsableAttribute ba = (BrowsableAttribute)pd.Attributes[typeof(BrowsableAttribute)];
                ba.GetType().GetField("browsable", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(ba, browsable);
            }
        }
    }

BLAKE2 ile Hash Oluşturma

BLAKE2, MD5, SHA-1, SHA-2 ve SHA-3’ten daha hızlı bir kriptografik hash algoritması olmakla birlikte, en son standart olan SHA-3 kadar güvenlidir. BLAKE2, yüksek hız, güvenlik ve basitlik nedeniyle birçok projeyle kabul edilmiştir.

BLAKE2’nin iki çeşit algoritması vardır:

  • BLAKE2b (veya sadece BLAKE2) 64 bitlik platformlar (NEON etkinleştirilmiş ARM’lar da dahil olmak üzere) için optimize edilmiştir ve 1 ile 64 bayt arasında herhangi bir boyutta özet üretir.
  • BLAKE2s 8 ila 32 bitlik platformlar için optimize edilmiştir ve 1 ila 32 bayt arasında herhangi bir boyutta özet üretir.

Daha fazla bilgi için https://blake2.net/ adresini ziyaret edebilirsiniz.

Github’daki C# kütüphanesini kullanarak, BLAKE2b ile minimum 8 bit, maksimum 512 bit boyutunda hash üretebiliyoruz. Aşağıdaki örnekte boyut 512 bit verildi. Blake2Sharp kütüphanesini https://github.com/BLAKE2/BLAKE2 adresinden indirebilirsiniz.

private static string Blake2Hash(string text)
{
    return BitConverter.ToString(Blake2B.ComputeHash(Encoding.UTF8.GetBytes(text), 
                                 new Blake2BConfig { OutputSizeInBits = 512 })).Replace("-", "");
}

REST Servis Çağrısı

REST GET/POST/PUT/DELETE yapmak için uyguladığım adımlar şu şekilde:

  • Visual Studio’da Visual C#/Windows Desktop/Console Application projesi oluşturdum. Adını RestClientSample koydum.
  • NuGet üzerinden Microsoft.AspNet.WebApi.Client paketini yükledim.
  • Rest servise gönderilecek ve servisten dönecek nesnelere karşılık gelen sınıfları oluşturdum.
  • Program.cs’yi aşağıdaki gibi değiştirdim.
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace RestClientSample
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                RunAsync().Wait();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.StackTrace);
            }

            Console.ReadKey();
        }

        static async Task RunAsync()
        {
            using (var client = new HttpClient())
            {
                // Servis adresi
                client.BaseAddress = new Uri("http://webadresi.com/");
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                // Servis Basic Http Authentication istiyorsa eklenir.
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(new UTF8Encoding().GetBytes(string.Format("{0}:{1}", "TestUser", "Pass"))));

                // HTTP GET
                HttpResponseMessage response = await client.GetAsync("api/IotDatas/1");
                if (response.IsSuccessStatusCode)
                {
                    IotData product = await response.Content.ReadAsAsync<IotData>();
                    Console.WriteLine("{0}\t${1}\t{2}", product.Name, product.Value, product.CreateDate);
                }

                // HTTP POST
                var iotData = new IotData { Name = "Test", Value = null, CreateDate = DateTime.Now };
                response = await client.PostAsJsonAsync("api/IotDatas", iotData);
                if (response.IsSuccessStatusCode)
                {
                    Uri gizmoUrl = response.Headers.Location;

                    // HTTP PUT
                    iotData.Value = null;
                    response = await client.PutAsJsonAsync(gizmoUrl, iotData);

                    // HTTP DELETE
                    response = await client.DeleteAsync(gizmoUrl);
                }
            }
        }
    }
}

String Format Kodları

Sayılar:
Specifier Type Format Output (Passed Double 1.42) Output (Passed Int -12400)
c Currency {0:c} $1.42 -$12,400
d Decimal (Whole number) {0:d} System.FormatException -12400
e Scientific {0:e} 1.420000e+000 -1.240000e+004
f Fixed point {0:f} 1.42 -12400.00
g General {0:g} 1.42 -12400
n Number with commas for thousands {0:n} 1.42 -12,400
r Round trippable {0:r} 1.42 System.FormatException
x Hexadecimal {0:x4} System.FormatException cf90

Özel Sayı Biçimlendirme:
Specifier Type Example Output (Passed Double 1500.42) Note
0 Zero placeholder {0:00.0000} 1500.4200 Pads with zeroes.
# Digit placeholder {0:(#).##} (1500).42
. Decimal point {0:0.0} 1500.4
, Thousand separator {0:0,0} 1,500 Must be between two zeroes.
,. Number scaling {0:0,.} 2 Comma adjacent to Period scales by 1000.
% Percent {0:0%} 150042% Multiplies by 100, adds % sign.
e Exponent placeholder {0:00e+0} 15e+2 Many exponent formats available.

Tarihler:
Specifier Type Example (Passed System.DateTime.Now)
d Short date 10/12/2002
D Long date December 10, 2002
t Short time 10:11 PM
T Long time 10:11:29 PM
f Full date & time December 10, 2002 10:11 PM
F Full date & time (long) December 10, 2002 10:11:29 PM
g Default date & time 10/12/2002 10:11 PM
G Default date & time (long) 10/12/2002 10:11:29 PM
M Month day pattern December 10
r RFC1123 date string Tue, 10 Dec 2002 22:11:29 GMT
s Sortable date string 2002-12-10T22:11:29
u Universal sortable, local time 2002-12-10 22:13:50Z
U Universal sortable, GMT December 11, 2002 3:13:50 AM
Y Year month pattern December, 2002

Özel Tarih Biçimlendirme:
Specifier Type Example Example Output
dd Day {0:dd} 10
ddd Day name {0:ddd} Tue
dddd Full day name {0:dddd} Tuesday
f, ff, … Second fractions {0:fff} 932
gg, … Era {0:gg} A.D.
hh 2 digit hour {0:hh} 10
HH 2 digit hour, 24hr format {0:HH} 22
mm Minute 00-59 {0:mm} 38
MM Month 01-12 {0:MM} 12
MMM Month abbreviation {0:MMM} Dec
MMMM Full month name {0:MMMM} December
ss Seconds 00-59 {0:ss} 46
tt AM or PM {0:tt} PM
yy Year, 2 digits {0:yy} 02
yyyy Year {0:yyyy} 2002
zz Timezone offset, 2 digits {0:zz} -05
zzz Full timezone offset {0:zzz} -05:00
: Separator {0:hh:mm:ss} 10:43:20
/ Separator {0:dd/MM/yyyy} 10/12/2002

Numaralandırmalar:
Specifier Type
g Default (Flag names if available, otherwise decimal)
f Flags always
d Integer always
x Eight digit hex.

Bazı Faydalı Örnekler:
String.Format(“{0:(###) ###-####}”, 8005551212); => (800) 555-1212