<?xml version="1.0" encoding="utf-8" standalone="no"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">
  <channel>
    <title>İlkay İlknur</title>
    <link>https://www.ilkayilknur.com/rss</link>
    <description>İlkay İlknur</description>
    <copyright>2023 İlkay İlknur</copyright>
    <managingEditor>ilkay@ilkayilknur.com</managingEditor>
    <language>en-us</language><itunes:explicit>no</itunes:explicit><itunes:subtitle>İlkay İlknur</itunes:subtitle><itunes:owner><itunes:email>ilkay@ilkayilknur.com</itunes:email></itunes:owner><item>
      <guid isPermaLink="false">the-new-code-analyzers-in-dotnet-6</guid>
      <link>https://www.ilkayilknur.com/the-new-code-analyzers-in-dotnet-6</link>
      <title>The New Code Analyzers In .NET 6</title>
      <description>&lt;p&gt;I think code analyzers and fixers are one of the most powerful features in the .NET ecosystem. You can implement your own or use the existing ones to write more reliable and performant code.&lt;/p&gt;&#13;
&lt;p&gt;In this post, we're going to look at the new code analyzers coming with .NET 6.&lt;/p&gt;&#13;
&lt;p&gt;&lt;em&gt;Quick reminder: By default most of the new analyzers are enabled at &lt;code&gt;Info&lt;/code&gt; level. You can enable these analyzers at &lt;code&gt;Warning&lt;/code&gt; level by &lt;a href="https://docs.microsoft.com/dotnet/fundamentals/code-analysis/overview#enable-additional-rules" target="_blank"&gt;configuring the analysis mode&lt;/a&gt; like this: &lt;code&gt;&amp;lt;AnalysisMode&amp;gt;AllEnabledByDefault&amp;lt;/AnalysisMode&amp;gt;&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;&#13;
&lt;h3 id="do-not-call-taskwhenall-with-a-single-argument"&gt;Do not call Task.WhenAll with a single argument&lt;/h3&gt;&#13;
&lt;p&gt;Instead of calling &lt;code&gt;Task.WhenAll &lt;/code&gt; method with a single argument, we can directly await the task.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var task = SomeAsyncOperation();&#13;
await Task.WhenAll(task);&#13;
//Await the task instead of Task.WhenAll&#13;
await task;&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h3 id="do-not-call-taskwaitall-with-a-single-argument"&gt;Do not call Task.WaitAll with a single argument&lt;/h3&gt;&#13;
&lt;p&gt;Instead of calling &lt;code&gt;Task.WaitAll&lt;/code&gt; method with a single argument, we can use &lt;code&gt;Task.Wait&lt;/code&gt;.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var task = SomeAsyncOperation();&#13;
Task.WaitAll(task);&#13;
//Use Task.Wait instead of Task.WaitAll&#13;
task.Wait();&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h3 id="use-the-new-environmentprocesspath-property-to-get-the-process-path"&gt;Use the new Environment.ProcessPath property to get the process path&lt;/h3&gt;&#13;
&lt;p&gt;.NET 6 introduces a new API(&lt;code&gt;Environment.ProcessPath&lt;/code&gt;) to get the process path. We can use it instead of &lt;code&gt;Process.GetCurrentProcess().MainModule.FileName&lt;/code&gt;.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var pPath = Process.GetCurrentProcess().MainModule.FileName;&#13;
//Use Environment.ProcessPath instead of Process.GetCurrentProcess().MainModule.FileName &#13;
pPath = Environment.ProcessPath;&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h3 id="use-environmentcurrentmanagedthreadid-to-get-the-managed-thread-id"&gt;Use Environment.CurrentManagedThreadId to get the managed thread Id&lt;/h3&gt;&#13;
&lt;p&gt;&lt;code&gt;Environment.CurrentManagedThreadId&lt;/code&gt; is more compact and efficient replacement of &lt;code&gt;Thread.CurrentThread.ManagedThreadId&lt;/code&gt;.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var threadId = Thread.CurrentThread.ManagedThreadId;&#13;
//Use Environment.CurrentManagedThreadId instead of Thread.CurrentThread.ManagedThreadId  &#13;
threadId = Environment.CurrentManagedThreadId;&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h3 id="use-stringcontainschar-instead-of-stringcontainsstring"&gt;Use String.Contains(char) instead of String.Contains(String)&lt;/h3&gt;&#13;
&lt;p&gt;It is efficient to replace calls to &lt;code&gt;String.Contains(String)&lt;/code&gt; method with string arguments containing a single &lt;code&gt;char&lt;/code&gt;.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var testStr = &amp;quot;Test&amp;quot;;&#13;
var contains = testStr.Contains(&amp;quot;t&amp;quot;);&#13;
//Use String.Contains(char) instead of String.Contains(string)&#13;
var contains = testStr.Contains('t');&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h3 id="replace-dictionarykeyscontains-calls-to-containskey"&gt;Replace Dictionary&amp;lt;,&amp;gt;.Keys.Contains calls to ContainsKey&lt;/h3&gt;&#13;
&lt;p&gt;Using &lt;code&gt;ContainsKey&lt;/code&gt; or &lt;code&gt;ContainsValue&lt;/code&gt; methods are more performant options when we want to check if a dictionary contains a key or value.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var dictionary = new Dictionary&amp;lt;string, string&amp;gt;();&#13;
var containsKey = dictionary.Keys.Contains(&amp;quot;test&amp;quot;);&#13;
//Use ContainsKey&#13;
containsKey = dictionary.ContainsKey(&amp;quot;test&amp;quot;);&#13;
&#13;
var containsValue = dictionary.Values.Contains(&amp;quot;test&amp;quot;);&#13;
//Use ContainsKey&#13;
containsValue = dictionary.ContainsValue(&amp;quot;test&amp;quot;);&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h3 id="use-span-based-stringconcat-to-avoid-allocations"&gt;Use span-based string.Concat to avoid allocations&lt;/h3&gt;&#13;
&lt;p&gt;We can use the span-based &lt;code&gt;string.Concat&lt;/code&gt; method in some string concatenation patterns and avoid unnecessary allocations.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var testStr = &amp;quot;Test&amp;quot;;&#13;
var newStr = testStr + testStr.Substring(2) + testStr.Substring(2, 1);&#13;
//Use span-based String.Concat&#13;
newStr = string.Concat(testStr, testStr.AsSpan(2), testStr.AsSpan(2, 1));&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h3 id="use-stringasspan-instead-of-stringsubstring-when-parsing"&gt;Use string.AsSpan() instead of string.Substring() when parsing&lt;/h3&gt;&#13;
&lt;p&gt;We can use &lt;code&gt;string.AsSpan&lt;/code&gt; method when calling parsing methods and avoid the allocation caused by the &lt;code&gt;string.Substring&lt;/code&gt; method.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var str = &amp;quot;1234566&amp;quot;;&#13;
var builder = new StringBuilder();&#13;
builder.Append(str.Substring(1));&#13;
&#13;
//Replace Substring with AsSpan&#13;
builder.Append(str.AsSpan(1));&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h3 id="call-async-methods-when-in-an-async-method"&gt;Call async methods when in an async method&lt;/h3&gt;&#13;
&lt;p&gt;We can use async APIs in task returning async methods.&lt;/p&gt;&#13;
&lt;p&gt;For example,&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;async Task SomeAsyncOperation(FileStream str)&#13;
{&#13;
    str.Read(buffer, 0, 10);&#13;
&#13;
    //Prefer ReadAsync&#13;
    await str.ReadAsync(buffer, 0, 10);&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h3 id="prefer-stringequals-instead-of-stringcompare-for-equality-checks"&gt;Prefer String.Equals instead of String.Compare for equality checks&lt;/h3&gt;&#13;
&lt;p&gt;It is more readable to use &lt;code&gt;String.Equals&lt;/code&gt; for equality checks instead of calling &lt;code&gt;String.Compare&lt;/code&gt; and checking the result.&lt;/p&gt;&#13;
&lt;p&gt;Consider the following code.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var str1 = &amp;quot;1234566&amp;quot;;&#13;
var str2 = &amp;quot;Test&amp;quot;;&#13;
&#13;
if(string.Compare(str1,str2) == 0)&#13;
{&#13;
&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;We can write the same logic with &lt;code&gt;string.Compare&lt;/code&gt; method.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var str1 = &amp;quot;1234566&amp;quot;;&#13;
var str2 = &amp;quot;Test&amp;quot;;&#13;
&#13;
if(string.Equals(str1, str2))&#13;
{&#13;
&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var str1 = &amp;quot;1234566&amp;quot;;&#13;
var str2 = &amp;quot;Test&amp;quot;;&#13;
&#13;
if(string.Equals(str1, str2))&#13;
{&#13;
&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h3 id="warn-developers-when-calling-bufferblockcopy"&gt;Warn developers when calling Buffer.BlockCopy&lt;/h3&gt;&#13;
&lt;p&gt;&lt;code&gt;Buffer.BlockCopy&lt;/code&gt; method expects the number of bytes to be copied for the &lt;code&gt;count&lt;/code&gt; argument. So passing &lt;code&gt;Array.Length&lt;/code&gt; could cause unexpected results.&lt;/p&gt;&#13;
&lt;p&gt;&lt;img src="https://az718566.vo.msecnd.net/uploads/2021/11/30/block-copy.png" alt="" /&gt;&lt;/p&gt;&#13;
&lt;h3 id="warn-developers-when-unknown-platform-names-are-used"&gt;Warn developers when unknown platform names are used&lt;/h3&gt;&#13;
&lt;p&gt;When developers use &lt;code&gt;SupportedOSPlatform&lt;/code&gt;, &lt;code&gt;UnsupportedOSPlatform&lt;/code&gt; attributes or &lt;code&gt;OperatingSystem.IsPlatform()&lt;/code&gt;, &lt;code&gt;OperatingSystem.IsPlatformVersionAtLeast()&lt;/code&gt; methods, they pass string literals for OS platform names. The new analyzer checks these usages and warns developers if an unknown flatform name is used.&lt;/p&gt;&#13;
&lt;p&gt;&lt;img src="https://az718566.vo.msecnd.net/uploads/2021/11/30/unknown-OS-platform.png" alt="" /&gt;&lt;/p&gt;&#13;
&lt;p&gt;See you next time!&lt;/p&gt;&#13;
</description>
    </item>
    <item>
      <guid isPermaLink="false">CallerArgumentExpression-attribute-in-csharp-10</guid>
      <link>https://www.ilkayilknur.com/CallerArgumentExpression-attribute-in-csharp-10</link>
      <title>CallerArgumentExpression Attribute In C# 10</title>
      <description>&lt;p&gt;Caller information attributes are one of the useful ways to obtain caller information to a method in C#. C# 10 brings a new member to the caller information attributes.&lt;/p&gt;&#13;
&lt;p&gt;Before mentioning the new member, let's see why the team had decided to implement it.&lt;/p&gt;&#13;
&lt;p&gt;Consider we have a &lt;code&gt;Exists&lt;/code&gt; method with multiple assertions.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;bool Exists(string[] array, int index)&#13;
{&#13;
    Debug.Assert(array != null);&#13;
    Debug.Assert(index &amp;gt;= 0);&#13;
&#13;
    return true;&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;When we call the &lt;code&gt;Exists&lt;/code&gt; method with an invalid argument such as a null array, the &lt;code&gt;Debug.Assert&lt;/code&gt; throws an exception, and the exception stack trace includes file path, line number, and method name.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-bash"&gt;Process terminated. Assertion failed.&#13;
   at Program.&amp;lt;&amp;lt;Main&amp;gt;$&amp;gt;g__Exists|0_0(String[] array, Int32 findIndex) in C:\Users\ilkay\source\repos\ConsoleApp34\ConsoleApp34\Program.cs:line 11&#13;
   at Program.&amp;lt;Main&amp;gt;$(String[] args) in C:\Users\ilkay\source\repos\ConsoleApp34\ConsoleApp34\Program.cs:line 7&#13;
&#13;
C:\Users\ilkay\source\repos\ConsoleApp34\ConsoleApp34\bin\Debug\net6.0\ConsoleApp34.exe (process 10336) exited with code -2146232797.&#13;
Press any key to close this window . . .&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;As you see, the stack trace isn't very helpful, and we need to open the source code and find the related line to see which assertion has failed. However, it would be great to see the failed expression in the exception message.&lt;/p&gt;&#13;
&lt;p&gt;In C# 10, you can use the new &lt;code&gt;CallerArgumentExpression&lt;/code&gt; attribute and capture the expression passed to a method. Let's write a simple &lt;code&gt;Assert&lt;/code&gt; method using the &lt;code&gt;CallerArgumentExpression&lt;/code&gt; attribute and use it in the &lt;code&gt;Exists&lt;/code&gt; method.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;void Assert(bool condition, [CallerArgumentExpression(&amp;quot;condition&amp;quot;)] string expression = default)&#13;
{&#13;
    if (!condition)&#13;
    {&#13;
        Console.WriteLine($&amp;quot;Condition has failed. Condition={expression}&amp;quot;);&#13;
    }&#13;
}&#13;
&#13;
bool Exists(string[] array, int index)&#13;
{&#13;
    Assert(array != null);&#13;
    Assert(index &amp;gt;= 0);&#13;
&#13;
    return true;&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;Calling the &lt;code&gt;Exists&lt;/code&gt; method with a null array argument will produce the following output in the console.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-bash"&gt;Condition has failed. Condition=array != null&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;.NET 6 introduces a new null checking API(&lt;code&gt;ArgumentNullException.ThrowIfNull&lt;/code&gt;) that uses the &lt;code&gt;CallerArgumentExpression&lt;/code&gt; to obtain the expression passed to the method.&lt;/p&gt;&#13;
&lt;p&gt;Source code of the &lt;code&gt;ThrowIfNull&lt;/code&gt; method.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class ArgumentNullException : ArgumentException&#13;
{&#13;
    /// &amp;lt;summary&amp;gt;Throws an &amp;lt;see cref=&amp;quot;ArgumentNullException&amp;quot;/&amp;gt; if &amp;lt;paramref name=&amp;quot;argument&amp;quot;/&amp;gt; is null.&amp;lt;/summary&amp;gt;&#13;
    /// &amp;lt;param name=&amp;quot;argument&amp;quot;&amp;gt;The reference type argument to validate as non-null.&amp;lt;/param&amp;gt;&#13;
    /// &amp;lt;param name=&amp;quot;paramName&amp;quot;&amp;gt;The name of the parameter with which &amp;lt;paramref name=&amp;quot;argument&amp;quot;/&amp;gt; corresponds.&amp;lt;/param&amp;gt;&#13;
    public static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpression(&amp;quot;argument&amp;quot;)] string? paramName = null)&#13;
    {&#13;
        if (argument is null)&#13;
        {&#13;
            Throw(paramName);&#13;
        }&#13;
    }&#13;
&#13;
    [DoesNotReturn]&#13;
    private static void Throw(string? paramName) =&amp;gt;&#13;
        throw new ArgumentNullException(paramName);&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;As you see, it is so easy to obtain the expression information passed to a method in C# 10. I think testing and validation libraries will use the &lt;code&gt;CallerArgumentExpression&lt;/code&gt; attribute to provide more meaningful assertion messages in the coming releases.&lt;/p&gt;&#13;
&lt;p&gt;See you next time!&lt;/p&gt;</description>
    </item>
    <item>
      <guid isPermaLink="false">how-to-convert-block-scoped-namespacees-to-file-scoped-namespaces</guid>
      <link>https://www.ilkayilknur.com/how-to-convert-block-scoped-namespacees-to-file-scoped-namespaces</link>
      <title>How To Convert Block Scoped Namespaces To File Scoped Namespaces</title>
      <description>&lt;p&gt;One of the themes of .NET 6 is writing less boilerplate code and helping developers focus on their code instead of ceremony coming with the programming language and the framework. The file scoped namespaces feature is one of C# 10 features that we can consider under that theme.&lt;/p&gt;&#13;
&lt;p&gt;Visual Studio offers a fix to convert a block-scoped namespace to a file scoped namespace. However, you can only apply it for the current document. Therefore, it's not possible to fix all occurrences in a project or a solution.&lt;/p&gt;&#13;
&lt;p&gt;&lt;img src="https://az718566.vo.msecnd.net/uploads/2021/10/22/vs-fix-dialog.png" alt="visual-studio-convert-to-file-scoped-fix-dialog" /&gt;&lt;/p&gt;&#13;
&lt;p&gt;On the other hand, it is possible to convert a file scoped namespace to a block-scoped namespace and fix all occurrences in a project or a solution.&lt;/p&gt;&#13;
&lt;p&gt;&lt;img src="https://az718566.vo.msecnd.net/uploads/2021/10/22/block-scoped-refactoring.png" alt="" /&gt;&lt;/p&gt;&#13;
&lt;p&gt;This is how the refactoring/fix pair works. The default namespace declaration preference is block scoped. So, Visual Studio provides refactoring for converting file scoped namespaces to block-scoped namespaces.&lt;/p&gt;&#13;
&lt;p&gt;There are a couple of ways to change the default namespace declaration preference.&lt;/p&gt;&#13;
&lt;h2 id="editorconfig-file"&gt;.editorconfig File&lt;/h2&gt;&#13;
&lt;p&gt;If your project or solution contains a &lt;code&gt;.editorconfig&lt;/code&gt; file, you can add the following line and make the file scoped namespaces the default preference.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-bash"&gt;csharp_style_namespace_declarations = file_scoped&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;After adding the line, Visual Studio will offer the &amp;quot;Convert to file-scoped namespace&amp;quot; refactoring and you can fix all occurrences in a project or a solution.&lt;/p&gt;&#13;
&lt;p&gt;&lt;img src="https://az718566.vo.msecnd.net/uploads/2021/10/22/convert-to-namespace-refactoring.png" alt="" /&gt;&lt;/p&gt;&#13;
&lt;h2 id="changing-visual-studio-code-style-preference"&gt;Changing Visual Studio Code Style Preference&lt;/h2&gt;&#13;
&lt;p&gt;If your project doesn't contain a &lt;code&gt;.editorconfig&lt;/code&gt; file or you look for an alternative way, you can change the code-style preference in Visual Studio globally so that namespace preference applies to all projects that you work on. To change the preference globally, open the Options window(&lt;strong&gt;Tools=&amp;gt;Options&lt;/strong&gt;). Navigate to the C# code style preferences by clicking &lt;strong&gt;Text Editor=&amp;gt;C#=&amp;gt;Code Style&lt;/strong&gt; and then change the related preference.&lt;/p&gt;&#13;
&lt;p&gt;&lt;img src="https://az718566.vo.msecnd.net/uploads/2021/10/22/visual-studio-namespace-preference.png" alt="" /&gt;&lt;/p&gt;&#13;
&lt;p&gt;I hope this post helps you to adapt to the new features coming with C# 10.&lt;/p&gt;</description>
    </item>
    <item>
      <guid isPermaLink="false">record-structs-in-csharp-10</guid>
      <link>https://www.ilkayilknur.com/record-structs-in-csharp-10</link>
      <title>Record Structs In C# 10</title>
      <description>&lt;p&gt;One of the most significant additions to C# 9 was records. Records help us to implement immutable types with value-based equality semantics.&lt;/p&gt;&#13;
&lt;p&gt;Declaring a record type is pretty straightforward. First, let&amp;#39;s declare a record type using the positional syntax.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class='language-csharp' lang='csharp'&gt;public record Person(string Name, string Surname, int Age);&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;Below is how the compiler rewrites when compiling the record type declared above.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class='language-csharp' lang='csharp'&gt;public class Person : IEquatable&amp;lt;Person&amp;gt;&#13;
{&#13;
    [CompilerGenerated]&#13;
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]&#13;
    private readonly string &amp;lt;Name&amp;gt;k__BackingField;&#13;
&#13;
    [CompilerGenerated]&#13;
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]&#13;
    private readonly string &amp;lt;Surname&amp;gt;k__BackingField;&#13;
&#13;
    [CompilerGenerated]&#13;
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]&#13;
    private readonly int &amp;lt;Age&amp;gt;k__BackingField;&#13;
&#13;
    [System.Runtime.CompilerServices.Nullable(1)]&#13;
    protected virtual Type EqualityContract&#13;
    {&#13;
        [System.Runtime.CompilerServices.NullableContext(1)]&#13;
        [CompilerGenerated]&#13;
        get&#13;
        {&#13;
            return typeof(Person);&#13;
        }&#13;
    }&#13;
&#13;
    public string Name&#13;
    {&#13;
        [CompilerGenerated]&#13;
        get&#13;
        {&#13;
            return &amp;lt;Name&amp;gt;k__BackingField;&#13;
        }&#13;
        [CompilerGenerated]&#13;
        init&#13;
        {&#13;
            &amp;lt;Name&amp;gt;k__BackingField = value;&#13;
        }&#13;
    }&#13;
&#13;
    public string Surname&#13;
    {&#13;
        [CompilerGenerated]&#13;
        get&#13;
        {&#13;
            return &amp;lt;Surname&amp;gt;k__BackingField;&#13;
        }&#13;
        [CompilerGenerated]&#13;
        init&#13;
        {&#13;
            &amp;lt;Surname&amp;gt;k__BackingField = value;&#13;
        }&#13;
    }&#13;
&#13;
    public int Age&#13;
    {&#13;
        [CompilerGenerated]&#13;
        get&#13;
        {&#13;
            return &amp;lt;Age&amp;gt;k__BackingField;&#13;
        }&#13;
        [CompilerGenerated]&#13;
        init&#13;
        {&#13;
            &amp;lt;Age&amp;gt;k__BackingField = value;&#13;
        }&#13;
    }&#13;
&#13;
    public Person(string Name, string Surname, int Age)&#13;
    {&#13;
        &amp;lt;Name&amp;gt;k__BackingField = Name;&#13;
        &amp;lt;Surname&amp;gt;k__BackingField = Surname;&#13;
        &amp;lt;Age&amp;gt;k__BackingField = Age;&#13;
        base..ctor();&#13;
    }&#13;
&#13;
    [System.Runtime.CompilerServices.NullableContext(1)]&#13;
    public override string ToString()&#13;
    {&#13;
        StringBuilder stringBuilder = new StringBuilder();&#13;
        stringBuilder.Append(&amp;quot;Person&amp;quot;);&#13;
        stringBuilder.Append(&amp;quot; { &amp;quot;);&#13;
        if (PrintMembers(stringBuilder))&#13;
        {&#13;
            stringBuilder.Append(&amp;#39; &amp;#39;);&#13;
        }&#13;
        stringBuilder.Append(&amp;#39;}&amp;#39;);&#13;
        return stringBuilder.ToString();&#13;
    }&#13;
&#13;
    [System.Runtime.CompilerServices.NullableContext(1)]&#13;
    protected virtual bool PrintMembers(StringBuilder builder)&#13;
    {&#13;
        RuntimeHelpers.EnsureSufficientExecutionStack();&#13;
        builder.Append(&amp;quot;Name = &amp;quot;);&#13;
        builder.Append((object)Name);&#13;
        builder.Append(&amp;quot;, Surname = &amp;quot;);&#13;
        builder.Append((object)Surname);&#13;
        builder.Append(&amp;quot;, Age = &amp;quot;);&#13;
        builder.Append(Age.ToString());&#13;
        return true;&#13;
    }&#13;
&#13;
    [System.Runtime.CompilerServices.NullableContext(2)]&#13;
    public static bool operator !=(Person left, Person right)&#13;
    {&#13;
        return !(left == right);&#13;
    }&#13;
&#13;
    [System.Runtime.CompilerServices.NullableContext(2)]&#13;
    public static bool operator ==(Person left, Person right)&#13;
    {&#13;
        return (object)left == right || ((object)left != null &amp;amp;&amp;amp; left.Equals(right));&#13;
    }&#13;
&#13;
    public override int GetHashCode()&#13;
    {&#13;
        return ((EqualityComparer&amp;lt;Type&amp;gt;.Default.GetHashCode(EqualityContract) * -1521134295 + EqualityComparer&amp;lt;string&amp;gt;.Default.GetHashCode(&amp;lt;Name&amp;gt;k__BackingField)) * -1521134295 + EqualityComparer&amp;lt;string&amp;gt;.Default.GetHashCode(&amp;lt;Surname&amp;gt;k__BackingField)) * -1521134295 + EqualityComparer&amp;lt;int&amp;gt;.Default.GetHashCode(&amp;lt;Age&amp;gt;k__BackingField);&#13;
    }&#13;
&#13;
    [System.Runtime.CompilerServices.NullableContext(2)]&#13;
    public override bool Equals(object obj)&#13;
    {&#13;
        return Equals(obj as Person);&#13;
    }&#13;
&#13;
    [System.Runtime.CompilerServices.NullableContext(2)]&#13;
    public virtual bool Equals(Person other)&#13;
    {&#13;
        return (object)this == other || ((object)other != null &amp;amp;&amp;amp; EqualityContract == other.EqualityContract &amp;amp;&amp;amp; EqualityComparer&amp;lt;string&amp;gt;.Default.Equals(&amp;lt;Name&amp;gt;k__BackingField, other.&amp;lt;Name&amp;gt;k__BackingField) &amp;amp;&amp;amp; EqualityComparer&amp;lt;string&amp;gt;.Default.Equals(&amp;lt;Surname&amp;gt;k__BackingField, other.&amp;lt;Surname&amp;gt;k__BackingField) &amp;amp;&amp;amp; EqualityComparer&amp;lt;int&amp;gt;.Default.Equals(&amp;lt;Age&amp;gt;k__BackingField, other.&amp;lt;Age&amp;gt;k__BackingField));&#13;
    }&#13;
&#13;
    [System.Runtime.CompilerServices.NullableContext(1)]&#13;
    public virtual Person &amp;lt;Clone&amp;gt;$()&#13;
    {&#13;
        return new Person(this);&#13;
    }&#13;
&#13;
    protected Person([System.Runtime.CompilerServices.Nullable(1)] Person original)&#13;
    {&#13;
        &amp;lt;Name&amp;gt;k__BackingField = original.&amp;lt;Name&amp;gt;k__BackingField;&#13;
        &amp;lt;Surname&amp;gt;k__BackingField = original.&amp;lt;Surname&amp;gt;k__BackingField;&#13;
        &amp;lt;Age&amp;gt;k__BackingField = original.&amp;lt;Age&amp;gt;k__BackingField;&#13;
    }&#13;
&#13;
    public void Deconstruct(out string Name, out string Surname, out int Age)&#13;
    {&#13;
        Name = this.Name;&#13;
        Surname = this.Surname;&#13;
        Age = this.Age;&#13;
    }&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;In C# 9, records were reference types(&lt;code&gt;class&lt;/code&gt;). In addition to record classes, C# 10 brings record structs and allows us to declare and use value type records(&lt;code&gt;struct&lt;/code&gt;). Declaring a record struct is pretty similar to declaring a record class.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class='language-csharp' lang='csharp'&gt;public record struct Person(string Name, string Surname, int Age);&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;Since you can declare record structs using the &lt;code&gt;record struct&lt;/code&gt; keywords, you can also use &lt;code&gt;record class&lt;/code&gt; in addition to &lt;code&gt;record&lt;/code&gt; to declare record classes in C# 10.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class='language-csharp' lang='csharp'&gt;//Record classes&#13;
public record Person(string Name, string Surname, int Age);&#13;
public record class Person(string Name, string Surname, int Age);&#13;
&#13;
//Record structs&#13;
public record struct Person(string name, string surname, int age);&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;Let&amp;#39;s have a look at the code compiler generates.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class='language-csharp' lang='csharp'&gt;public struct Person : IEquatable&amp;lt;Person&amp;gt;&#13;
{&#13;
    [CompilerGenerated]&#13;
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]&#13;
    private string &amp;lt;Name&amp;gt;k__BackingField;&#13;
&#13;
    [CompilerGenerated]&#13;
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]&#13;
    private string &amp;lt;Surname&amp;gt;k__BackingField;&#13;
&#13;
    [CompilerGenerated]&#13;
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]&#13;
    private int &amp;lt;Age&amp;gt;k__BackingField;&#13;
&#13;
    public string Name&#13;
    {&#13;
        [IsReadOnly]&#13;
        [CompilerGenerated]&#13;
        get&#13;
        {&#13;
            return &amp;lt;Name&amp;gt;k__BackingField;&#13;
        }&#13;
        [CompilerGenerated]&#13;
        set&#13;
        {&#13;
            &amp;lt;Name&amp;gt;k__BackingField = value;&#13;
        }&#13;
    }&#13;
&#13;
    public string Surname&#13;
    {&#13;
        [IsReadOnly]&#13;
        [CompilerGenerated]&#13;
        get&#13;
        {&#13;
            return &amp;lt;Surname&amp;gt;k__BackingField;&#13;
        }&#13;
        [CompilerGenerated]&#13;
        set&#13;
        {&#13;
            &amp;lt;Surname&amp;gt;k__BackingField = value;&#13;
        }&#13;
    }&#13;
&#13;
    public int Age&#13;
    {&#13;
        [IsReadOnly]&#13;
        [CompilerGenerated]&#13;
        get&#13;
        {&#13;
            return &amp;lt;Age&amp;gt;k__BackingField;&#13;
        }&#13;
        [CompilerGenerated]&#13;
        set&#13;
        {&#13;
            &amp;lt;Age&amp;gt;k__BackingField = value;&#13;
        }&#13;
    }&#13;
&#13;
    public Person(string Name, string Surname, int Age)&#13;
    {&#13;
        &amp;lt;Name&amp;gt;k__BackingField = Name;&#13;
        &amp;lt;Surname&amp;gt;k__BackingField = Surname;&#13;
        &amp;lt;Age&amp;gt;k__BackingField = Age;&#13;
    }&#13;
&#13;
    [IsReadOnly]&#13;
    public override string ToString()&#13;
    {&#13;
        StringBuilder stringBuilder = new StringBuilder();&#13;
        stringBuilder.Append(&amp;quot;Person&amp;quot;);&#13;
        stringBuilder.Append(&amp;quot; { &amp;quot;);&#13;
        if (PrintMembers(stringBuilder))&#13;
        {&#13;
            stringBuilder.Append(&amp;#39; &amp;#39;);&#13;
        }&#13;
        stringBuilder.Append(&amp;#39;}&amp;#39;);&#13;
        return stringBuilder.ToString();&#13;
    }&#13;
&#13;
    [IsReadOnly]&#13;
    private bool PrintMembers(StringBuilder builder)&#13;
    {&#13;
        builder.Append(&amp;quot;Name = &amp;quot;);&#13;
        builder.Append((object)Name);&#13;
        builder.Append(&amp;quot;, Surname = &amp;quot;);&#13;
        builder.Append((object)Surname);&#13;
        builder.Append(&amp;quot;, Age = &amp;quot;);&#13;
        builder.Append(Age.ToString());&#13;
        return true;&#13;
    }&#13;
&#13;
    public static bool operator !=(Person left, Person right)&#13;
    {&#13;
        return !(left == right);&#13;
    }&#13;
&#13;
    public static bool operator ==(Person left, Person right)&#13;
    {&#13;
        return left.Equals(right);&#13;
    }&#13;
&#13;
    [IsReadOnly]&#13;
    public override int GetHashCode()&#13;
    {&#13;
        return (EqualityComparer&amp;lt;string&amp;gt;.Default.GetHashCode(&amp;lt;Name&amp;gt;k__BackingField) * -1521134295 + EqualityComparer&amp;lt;string&amp;gt;.Default.GetHashCode(&amp;lt;Surname&amp;gt;k__BackingField)) * -1521134295 + EqualityComparer&amp;lt;int&amp;gt;.Default.GetHashCode(&amp;lt;Age&amp;gt;k__BackingField);&#13;
    }&#13;
&#13;
    [IsReadOnly]&#13;
    public override bool Equals(object obj)&#13;
    {&#13;
        return obj is Person &amp;amp;&amp;amp; Equals((Person)obj);&#13;
    }&#13;
&#13;
    [IsReadOnly]&#13;
    public bool Equals(Person other)&#13;
    {&#13;
        return EqualityComparer&amp;lt;string&amp;gt;.Default.Equals(&amp;lt;Name&amp;gt;k__BackingField, other.&amp;lt;Name&amp;gt;k__BackingField) &amp;amp;&amp;amp; EqualityComparer&amp;lt;string&amp;gt;.Default.Equals(&amp;lt;Surname&amp;gt;k__BackingField, other.&amp;lt;Surname&amp;gt;k__BackingField) &amp;amp;&amp;amp; EqualityComparer&amp;lt;int&amp;gt;.Default.Equals(&amp;lt;Age&amp;gt;k__BackingField, other.&amp;lt;Age&amp;gt;k__BackingField);&#13;
    }&#13;
&#13;
    [IsReadOnly]&#13;
    public void Deconstruct(out string Name, out string Surname, out int Age)&#13;
    {&#13;
        Name = this.Name;&#13;
        Surname = this.Surname;&#13;
        Age = this.Age;&#13;
    }&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;Most of the generated code looks similar to the record classes. However, there is one difference between record structs and record classes. &lt;strong&gt;The generated properties in record structs are mutable by default.&lt;/strong&gt; The reason behind this decision was to be consistent with tuples. Tuples are like anonymous record structs with similar features.&#13;
On the other hand, struct mutability does not carry the same level of concern as class mutability does. That&amp;#39;s why the C# team decided to make record struct properties mutable by default. However, if you need immutable record structs, you can use the &lt;code&gt;readonly&lt;/code&gt; keyword. &lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class='language-csharp' lang='csharp'&gt;public readonly record struct Person(string name, string surname, int age);&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;Generated code&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class='language-csharp' lang='csharp'&gt;public struct Person : IEquatable&amp;lt;Person&amp;gt;&#13;
{&#13;
    [CompilerGenerated]&#13;
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]&#13;
    private string &amp;lt;name&amp;gt;k__BackingField;&#13;
&#13;
    [CompilerGenerated]&#13;
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]&#13;
    private string &amp;lt;surname&amp;gt;k__BackingField;&#13;
&#13;
    [CompilerGenerated]&#13;
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]&#13;
    private int &amp;lt;age&amp;gt;k__BackingField;&#13;
&#13;
    public string name&#13;
    {&#13;
        [IsReadOnly]&#13;
        [CompilerGenerated]&#13;
        get&#13;
        {&#13;
            return &amp;lt;name&amp;gt;k__BackingField;&#13;
        }&#13;
        [CompilerGenerated]&#13;
        set&#13;
        {&#13;
            &amp;lt;name&amp;gt;k__BackingField = value;&#13;
        }&#13;
    }&#13;
&#13;
    public string surname&#13;
    {&#13;
        [IsReadOnly]&#13;
        [CompilerGenerated]&#13;
        get&#13;
        {&#13;
            return &amp;lt;surname&amp;gt;k__BackingField;&#13;
        }&#13;
        [CompilerGenerated]&#13;
        set&#13;
        {&#13;
            &amp;lt;surname&amp;gt;k__BackingField = value;&#13;
        }&#13;
    }&#13;
&#13;
    public int age&#13;
    {&#13;
        [IsReadOnly]&#13;
        [CompilerGenerated]&#13;
        get&#13;
        {&#13;
            return &amp;lt;age&amp;gt;k__BackingField;&#13;
        }&#13;
        [CompilerGenerated]&#13;
        set&#13;
        {&#13;
            &amp;lt;age&amp;gt;k__BackingField = value;&#13;
        }&#13;
    }&#13;
&#13;
    public Person(string name, string surname, int age)&#13;
    {&#13;
        &amp;lt;name&amp;gt;k__BackingField = name;&#13;
        &amp;lt;surname&amp;gt;k__BackingField = surname;&#13;
        &amp;lt;age&amp;gt;k__BackingField = age;&#13;
    }&#13;
&#13;
    [IsReadOnly]&#13;
    public override string ToString()&#13;
    {&#13;
        StringBuilder stringBuilder = new StringBuilder();&#13;
        stringBuilder.Append(&amp;quot;Person&amp;quot;);&#13;
        stringBuilder.Append(&amp;quot; { &amp;quot;);&#13;
        if (PrintMembers(stringBuilder))&#13;
        {&#13;
            stringBuilder.Append(&amp;#39; &amp;#39;);&#13;
        }&#13;
        stringBuilder.Append(&amp;#39;}&amp;#39;);&#13;
        return stringBuilder.ToString();&#13;
    }&#13;
&#13;
    [IsReadOnly]&#13;
    private bool PrintMembers(StringBuilder builder)&#13;
    {&#13;
        builder.Append(&amp;quot;name = &amp;quot;);&#13;
        builder.Append((object)name);&#13;
        builder.Append(&amp;quot;, surname = &amp;quot;);&#13;
        builder.Append((object)surname);&#13;
        builder.Append(&amp;quot;, age = &amp;quot;);&#13;
        builder.Append(age.ToString());&#13;
        return true;&#13;
    }&#13;
&#13;
    public static bool operator !=(Person left, Person right)&#13;
    {&#13;
        return !(left == right);&#13;
    }&#13;
&#13;
    public static bool operator ==(Person left, Person right)&#13;
    {&#13;
        return left.Equals(right);&#13;
    }&#13;
&#13;
    [IsReadOnly]&#13;
    public override int GetHashCode()&#13;
    {&#13;
        return (EqualityComparer&amp;lt;string&amp;gt;.Default.GetHashCode(&amp;lt;name&amp;gt;k__BackingField) * -1521134295 + EqualityComparer&amp;lt;string&amp;gt;.Default.GetHashCode(&amp;lt;surname&amp;gt;k__BackingField)) * -1521134295 + EqualityComparer&amp;lt;int&amp;gt;.Default.GetHashCode(&amp;lt;age&amp;gt;k__BackingField);&#13;
    }&#13;
&#13;
    [IsReadOnly]&#13;
    public override bool Equals(object obj)&#13;
    {&#13;
        return obj is Person &amp;amp;&amp;amp; Equals((Person)obj);&#13;
    }&#13;
&#13;
    [IsReadOnly]&#13;
    public bool Equals(Person other)&#13;
    {&#13;
        return EqualityComparer&amp;lt;string&amp;gt;.Default.Equals(&amp;lt;name&amp;gt;k__BackingField, other.&amp;lt;name&amp;gt;k__BackingField) &amp;amp;&amp;amp; EqualityComparer&amp;lt;string&amp;gt;.Default.Equals(&amp;lt;surname&amp;gt;k__BackingField, other.&amp;lt;surname&amp;gt;k__BackingField) &amp;amp;&amp;amp; EqualityComparer&amp;lt;int&amp;gt;.Default.Equals(&amp;lt;age&amp;gt;k__BackingField, other.&amp;lt;age&amp;gt;k__BackingField);&#13;
    }&#13;
&#13;
    [IsReadOnly]&#13;
    public void Deconstruct(out string name, out string surname, out int age)&#13;
    {&#13;
        name = this.name;&#13;
        surname = this.surname;&#13;
        age = this.age;&#13;
    }&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h2 id='with-expressions'&gt;with Expressions&lt;/h2&gt;&#13;
&lt;p&gt;Record structs support &lt;code&gt;with&lt;/code&gt; expressions to create copies. In C# 10, we can use the &lt;code&gt;with&lt;/code&gt; expressions with all struct types including tuples.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class='language-csharp' lang='csharp'&gt;//with expressions - record structs&#13;
var p = new Person(&amp;quot;Ilkay&amp;quot;, &amp;quot;Ilknur&amp;quot;, 33);&#13;
var newP = p with { Age = 50 };&#13;
&#13;
public record struct Person(string Name, string Surname, int Age);&#13;
&#13;
// with expressions - regular structs&#13;
var pStruct = new PersonStruct()&#13;
{&#13;
    Name = &amp;quot;Ilkay&amp;quot;&#13;
};&#13;
&#13;
var pNewStruct = pStruct with { Name = &amp;quot;John&amp;quot; };&#13;
&#13;
public struct PersonStruct&#13;
{&#13;
    public string Name;&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h2 id='deconstruction'&gt;Deconstruction&lt;/h2&gt;&#13;
&lt;p&gt;Positional record structs support deconstruction similar to positional record classes. &lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class='language-csharp' lang='csharp'&gt;var p = new Person(&amp;quot;Ilkay&amp;quot;, &amp;quot;Ilknur&amp;quot;, 33);&#13;
var (name, surname, age) = p;&#13;
&#13;
public record struct Person(string Name, string Surname, int Age);&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h2 id='tostring-method'&gt;ToString Method&lt;/h2&gt;&#13;
&lt;p&gt;The synthesized &lt;code&gt;ToString&lt;/code&gt; method prints the type name and values.  &lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class='language-csharp' lang='csharp'&gt;var p = new Person(&amp;quot;Ilkay&amp;quot;, &amp;quot;Ilknur&amp;quot;, 33);&#13;
Console.WriteLine(p);&#13;
// prints : Person { Name = Ilkay, Surname = Ilknur, Age = 33 }&#13;
&#13;
public record struct Person(string Name, string Surname, int Age);&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;Record structs are regular structs, and all struct rules apply to record structs as well. However, there are some additional limitations and rules we need to keep in mind.&lt;/p&gt;&#13;
&lt;ul&gt;&#13;
&lt;li&gt;Record structs cannot use &lt;code&gt;ref&lt;/code&gt; modifier.&lt;/li&gt;&#13;
&lt;li&gt;Record struct parameters cannot use &lt;code&gt;ref&lt;/code&gt;, &lt;code&gt;out&lt;/code&gt; or &lt;code&gt;this&lt;/code&gt; modifiers (but &lt;code&gt;in&lt;/code&gt; and &lt;code&gt;params&lt;/code&gt; are allowed).&lt;/li&gt;&#13;
&lt;li&gt;Record structs inherit from &lt;code&gt;ValueTuple&lt;/code&gt; implicitly.&lt;/li&gt;&#13;
&#13;
&lt;/ul&gt;</description>
    </item>
    <item>
      <guid isPermaLink="false">a-new-modern-timer-api-in-dotnet-6-periodictimer</guid>
      <link>https://www.ilkayilknur.com/a-new-modern-timer-api-in-dotnet-6-periodictimer</link>
      <title>A New Modern Timer API In .NET 6 - PeriodicTimer</title>
      <description>&lt;p&gt;As a developer, you've probably used timers in .NET before. There are plenty of types of timers in .NET right now, and each of them serves a different purpose.&lt;/p&gt;&#13;
&lt;p&gt;List of types of timers in .NET 5.&lt;/p&gt;&#13;
&lt;ul&gt;&#13;
&lt;li&gt;&lt;code&gt;System.Threading.Timer&lt;/code&gt;&lt;/li&gt;&#13;
&lt;li&gt;&lt;code&gt;System.Timers.Timer&lt;/code&gt;&lt;/li&gt;&#13;
&lt;li&gt;&lt;code&gt;System.Windows.Forms.Timer&lt;/code&gt;&lt;/li&gt;&#13;
&lt;li&gt;&lt;code&gt;System.Web.UI.Timer&lt;/code&gt;&lt;/li&gt;&#13;
&lt;li&gt;&lt;code&gt;System.Windows.Threading.DispatcherTimer&lt;/code&gt;&lt;/li&gt;&#13;
&lt;/ul&gt;&#13;
&lt;p&gt;.NET 6 introduces a new timer type called &lt;code&gt;PeriodicTimer&lt;/code&gt;. The main purpose behind the &lt;code&gt;PeriodicTimer&lt;/code&gt; is to avoid using callbacks. Avoiding callbacks can save us from dealing with memory leaks that might occur in long-lived operations, and we can write &lt;code&gt;async&lt;/code&gt; code instead of using the &lt;code&gt;sync over async&lt;/code&gt; approach in callbacks. Another problem that you might deal with with the current timer types is callback overlapping. If you don't write code for the callback overlap scenario, you can see unexpected behaviors in your application.&lt;/p&gt;&#13;
&lt;p&gt;Creating a &lt;code&gt;PeriodicTimer&lt;/code&gt; instance is pretty straightforward. The only parameter you need to provide is the &lt;code&gt;period&lt;/code&gt; value.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var timer = new PeriodicTimer(TimeSpan.FromSeconds(10));&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;You can call the &lt;code&gt;WaitForNextTickAsync&lt;/code&gt; method to wait asynchronously between ticks and, write asynchronous code without dealing with callback overlapping problems because the &lt;code&gt;PeriodicTimer&lt;/code&gt; pauses while the business code is running and resumes when the &lt;code&gt;WaitForNextTickAsync&lt;/code&gt; method is called.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var timer = new PeriodicTimer(TimeSpan.FromSeconds(10));&#13;
&#13;
while (await timer.WaitForNextTickAsync())&#13;
{&#13;
    //Business logic&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;You can stop &lt;code&gt;PeriodicTimer&lt;/code&gt; using the &lt;code&gt;CancellationToken&lt;/code&gt; that you can specify while calling the &lt;code&gt;WaitForNextTickAsync&lt;/code&gt; method. Another way to stop the &lt;code&gt;PeriodicTimer&lt;/code&gt; is calling the &lt;code&gt;Dispose&lt;/code&gt; method. When the &lt;code&gt;Dispose&lt;/code&gt; method is called, &lt;code&gt;WaitForNextTickAsync&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt;, and you can stop executing the timer-based business logic.&lt;/p&gt;&#13;
&lt;p&gt;Some notes about &lt;code&gt;PeriodicTimer&lt;/code&gt;&lt;/p&gt;&#13;
&lt;ul&gt;&#13;
&lt;li&gt;The execution context isn't captured.&lt;/li&gt;&#13;
&lt;li&gt;&lt;code&gt;PeriodicTimer&lt;/code&gt; is intended to be used only by a single consumer at a time.&lt;/li&gt;&#13;
&lt;/ul&gt;&#13;
&lt;p&gt;PeriodicTimer documentation: &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.threading.periodictimer" target="_blank"&gt;https://docs.microsoft.com/en-us/dotnet/api/system.threading.periodictimer&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <guid isPermaLink="false">dotnet-6-ile-beraber-gelecek-olan-yeni-linq-apilari</guid>
      <link>https://www.ilkayilknur.com/dotnet-6-ile-beraber-gelecek-olan-yeni-linq-apilari</link>
      <title>.NET 6 İle Gelecek Olan Yeni LINQ API'ları</title>
      <description>&lt;p&gt;.NET içerisinde uzun süredir varolan ve sıklıkla kullanılan özelliklerden biri de LINQ. Geçtiğimiz versiyonlarda LINQ tarafına pek yeni özellik eklenmese de performans ve memory kullanımı bakımından pek çok iyileştirme yapılmıştı. .NET 6 ile beraber ise LINQ tarafında uzun süredir istenilen bazı API'lar bu versiyonla beraber hayatımıza giriyor olacak. Şimdi lafı çok fazla uzatmadan yeni gelecek LINQ API'larını inceleyelim.&lt;/p&gt;&#13;
&lt;h2 id="index-ve-range-desteği"&gt;Index Ve Range Desteği&lt;/h2&gt;&#13;
&lt;p&gt;Daha önce blogda C# 8 ile beraber gelen &lt;a href="https://www.ilkayilknur.com/csharp-8-0-ile-daha-kolay-indeksleme-ve-slicing" target="_blank"&gt;Index ve Range desteğinden&lt;/a&gt; bahsetmiştim. .NET 6 ile beraber index ve range desteği çeşitli LINQ API'larına da ekleniyor.&lt;/p&gt;&#13;
&lt;p&gt;Eklenen metotlara kısaca bir bakalım.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;namespace System.Linq&#13;
{&#13;
    public static partial class Enumerable&#13;
    {&#13;
        public static TSource ElementAt&amp;lt;TSource&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, Index index);&#13;
        public static TSource ElementAtOrDefault&amp;lt;TSource&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, Index index);&#13;
        public static IEnumerable&amp;lt;TSource&amp;gt; Take&amp;lt;TSource&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, Range range);&#13;
    }&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;namespace System.Linq&#13;
{&#13;
    public static partial class Queryable&#13;
    {&#13;
        public static TSource ElementAt&amp;lt;TSource&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source, Index index);&#13;
        public static TSource ElementAtOrDefault&amp;lt;TSource&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source, Index index);&#13;
        public static IQueryable&amp;lt;TSource&amp;gt; Take&amp;lt;TSource&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source, Range range);&#13;
    }&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;Ufak bir örnek yapalım.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var list = new List&amp;lt;int&amp;gt;()&#13;
{&#13;
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12&#13;
};&#13;
&#13;
var elements = list.ElementAt(^2);&#13;
var elements2 = list.Take(2..);&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h2 id="default-değer-belirtmeyi-sağlayan-singleordefault-lastordefault-ve-firstordefault-metot-overloadları"&gt;Default Değer Belirtmeyi Sağlayan SingleOrDefault, LastOrDefault ve FirstOrDefault Metot Overloadları&lt;/h2&gt;&#13;
&lt;p&gt;.NET 6 ile beraber &lt;code&gt;SingleOrDefault&lt;/code&gt;, &lt;code&gt;LastOrDefault&lt;/code&gt; ve &lt;code&gt;FirstOrDefault&lt;/code&gt; metotlarını kullandığımızda ek olarak kendi istediğimiz bir default değeri dönmesini sağlayabiliyoruz.&lt;/p&gt;&#13;
&lt;p&gt;Eklenen API'lar şu şekilde.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;namespace System.Linq&#13;
{&#13;
    public static class Enumerable&#13;
    {&#13;
        public static TSource SingleOrDefault&amp;lt;TSource&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, TSource defaultValue);&#13;
        public static TSource SingleOrDefault&amp;lt;TSource&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, Func&amp;lt;TSource, bool&amp;gt; predicate, TSource defaultValue);&#13;
&#13;
        public static TSource FirstOrDefault&amp;lt;TSource&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, TSource defaultValue);&#13;
        public static TSource FirstOrDefault&amp;lt;TSource&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, Func&amp;lt;TSource, bool&amp;gt; predicate, TSource defaultValue);&#13;
&#13;
        public static TSource LastOrDefault&amp;lt;TSource&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, TSource defaultValue);&#13;
        public static TSource LastOrDefault&amp;lt;TSource&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, Func&amp;lt;TSource, bool&amp;gt; predicate, TSource defaultValue);&#13;
    }&#13;
&#13;
    public static class Queryable&#13;
    {&#13;
        public static TSource SingleOrDefault&amp;lt;TSource&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source, TSource defaultValue);&#13;
        public static TSource SingleOrDefault&amp;lt;TSource&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source, Func&amp;lt;TSource, bool&amp;gt; predicate, TSource defaultValue);&#13;
&#13;
        public static TSource FirstOrDefault&amp;lt;TSource&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source, TSource defaultValue);&#13;
        public static TSource FirstOrDefault&amp;lt;TSource&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source, Func&amp;lt;TSource, bool&amp;gt; predicate, TSource defaultValue);&#13;
&#13;
        public static TSource LastOrDefault&amp;lt;TSource&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source, TSource defaultValue);&#13;
        public static TSource LastOrDefault&amp;lt;TSource&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source, Func&amp;lt;TSource, bool&amp;gt; predicate, TSource defaultValue);&#13;
    }&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;Bir örnek yapalım.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var list = new List&amp;lt;int&amp;gt;()&#13;
{&#13;
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12&#13;
};&#13;
&#13;
var firstElement = list.FirstOrDefault(-1); //returns 1&#13;
var element = list.FirstOrDefault(t =&amp;gt; t &amp;gt; 13, -1); //returns -1 instead of default int&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h2 id="distinct-except-intersect-union-min-ve-max-İçin-by-metotları"&gt;Distinct, Except, Intersect, Union, Min ve Max İçin *By Metotları&lt;/h2&gt;&#13;
&lt;p&gt;.NET 6 ile beraber yeni eklenecek API'ların bir diğeri de &lt;code&gt;keySelector&lt;/code&gt; belirtmemizi sağlayan *By metotları.&lt;/p&gt;&#13;
&lt;p&gt;İlk olarak eklenen metotları görelim.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;namespace System.Linq&#13;
{&#13;
    public static class Enumerable&#13;
    {&#13;
        public static IEnumerable&amp;lt;TSource&amp;gt; DistinctBy&amp;lt;TSource, TKey&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, Func&amp;lt;TSource, TKey&amp;gt; keySelector);&#13;
        public static IEnumerable&amp;lt;TSource&amp;gt; DistinctBy&amp;lt;TSource, TKey&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, Func&amp;lt;TSource, TKey&amp;gt; keySelector, IEqualityComparer&amp;lt;TKey&amp;gt;? comparer);&#13;
        &#13;
        public static IEnumerable&amp;lt;TSource&amp;gt; ExceptBy&amp;lt;TSource, TKey&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; first, IEnumerable&amp;lt;TSource&amp;gt; second, Func&amp;lt;TSource, TKey&amp;gt; keySelector);&#13;
        public static IEnumerable&amp;lt;TSource&amp;gt; ExceptBy&amp;lt;TSource, TKey&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; first, IEnumerable&amp;lt;TSource&amp;gt; second, Func&amp;lt;TSource, TKey&amp;gt; keySelector, IEqualityComparer&amp;lt;TKey&amp;gt;? comparer);&#13;
        public static IEnumerable&amp;lt;TSource&amp;gt; ExceptBy&amp;lt;TSource, TKey&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; first, IEnumerable&amp;lt;TKey&amp;gt; second, Func&amp;lt;TSource, TKey&amp;gt; keySelectorFirst);&#13;
        public static IEnumerable&amp;lt;TSource&amp;gt; ExceptBy&amp;lt;TSource, TKey&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; first, IEnumerable&amp;lt;TKey&amp;gt; second, Func&amp;lt;TSource, TKey&amp;gt; keySelectorFirst, IEqualityComparer&amp;lt;TKey&amp;gt;? comparer);&#13;
&#13;
        public static IEnumerable&amp;lt;TSource&amp;gt; IntersectBy&amp;lt;TSource, TKey&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; first, IEnumerable&amp;lt;TSource&amp;gt; second, Func&amp;lt;TSource, TKey&amp;gt; keySelector);&#13;
        public static IEnumerable&amp;lt;TSource&amp;gt; IntersectBy&amp;lt;TSource, TKey&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; first, IEnumerable&amp;lt;TSource&amp;gt; second, Func&amp;lt;TSource, TKey&amp;gt; keySelector, IEqualityComparer&amp;lt;TKey&amp;gt;? comparer);&#13;
        public static IEnumerable&amp;lt;TSource&amp;gt; IntersectBy&amp;lt;TSource, TKey&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; first, IEnumerable&amp;lt;TKey&amp;gt; second, Func&amp;lt;TSource, TKey&amp;gt; keySelectorFirst);&#13;
        public static IEnumerable&amp;lt;TSource&amp;gt; IntersectBy&amp;lt;TSource, TKey&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; first, IEnumerable&amp;lt;TKey&amp;gt; second, Func&amp;lt;TSource, TKey&amp;gt; keySelectorFirst, IEqualityComparer&amp;lt;TKey&amp;gt;? comparer);&#13;
        &#13;
        public static IEnumerable&amp;lt;TSource&amp;gt; UnionBy&amp;lt;TSource, TKey&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; first, IEnumerable&amp;lt;TSource&amp;gt; second, Func&amp;lt;TSource, TKey&amp;gt; keySelector);&#13;
        public static IEnumerable&amp;lt;TSource&amp;gt; UnionBy&amp;lt;TSource, TKey&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; first, IEnumerable&amp;lt;TSource&amp;gt; second, Func&amp;lt;TSource, TKey&amp;gt; keySelector, IEqualityComparer&amp;lt;TKey&amp;gt;? comparer);&#13;
        &#13;
        public static TSource MinBy&amp;lt;TSource, TResult&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, Func&amp;lt;TSource, TResult&amp;gt; selector);&#13;
        public static TSource MinBy&amp;lt;TSource, TResult&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, Func&amp;lt;TSource, TResult&amp;gt; selector, IComparer&amp;lt;TResult&amp;gt;? comparer);&#13;
        &#13;
        public static TSource MaxBy&amp;lt;TSource, TResult&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, Func&amp;lt;TSource, TResult&amp;gt; selector);&#13;
        public static TSource MaxBy&amp;lt;TSource, TResult&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, Func&amp;lt;TSource, TResult&amp;gt; selector, IComparer&amp;lt;TResult&amp;gt;? comparer);&#13;
        &#13;
        // Missing min &amp;amp; max overloads accepting custom comparers added for completeness&#13;
        public static TResult Min&amp;lt;TSource, TResult&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, IComparer&amp;lt;TResult&amp;gt;? comparer);&#13;
        public static TResult Max&amp;lt;TSource, TResult&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, IComparer&amp;lt;TResult&amp;gt;? comparer);&#13;
    }&#13;
&#13;
    public static class Queryable&#13;
    {&#13;
        public static IQueryable&amp;lt;TSource&amp;gt; DistinctBy&amp;lt;TSource, TKey&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source, Expression&amp;lt;Func&amp;lt;TSource, TKey&amp;gt;&amp;gt; keySelector);&#13;
        public static IQueryable&amp;lt;TSource&amp;gt; DistinctBy&amp;lt;TSource, TKey&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source, Expression&amp;lt;Func&amp;lt;TSource, TKey&amp;gt;&amp;gt; keySelector, IEqualityComparer&amp;lt;TKey&amp;gt;? comparer);&#13;
&#13;
        public static IQueryable&amp;lt;TSource&amp;gt; ExceptBy&amp;lt;TSource, TKey&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source1, IEnumerable&amp;lt;TSource&amp;gt; source2, Expression&amp;lt;Func&amp;lt;TSource, TKey&amp;gt;&amp;gt; keySelector);&#13;
        public static IQueryable&amp;lt;TSource&amp;gt; ExceptBy&amp;lt;TSource, TKey&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source1, IEnumerable&amp;lt;TSource&amp;gt; source2, Expression&amp;lt;Func&amp;lt;TSource, TKey&amp;gt;&amp;gt; keySelector, IEqualityComparer&amp;lt;TKey&amp;gt;? comparer);&#13;
        public static IQueryable&amp;lt;TSource&amp;gt; ExceptBy&amp;lt;TSource, TKey&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source1, IEnumerable&amp;lt;TKey&amp;gt; source2, Expression&amp;lt;Func&amp;lt;TSource, TKey&amp;gt;&amp;gt; keySelectorFirst);&#13;
        public static IQueryable&amp;lt;TSource&amp;gt; ExceptBy&amp;lt;TSource, TKey&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source1, IEnumerable&amp;lt;TKey&amp;gt; source2, Expression&amp;lt;Func&amp;lt;TSource, TKey&amp;gt;&amp;gt; keySelectorFirst, IEqualityComparer&amp;lt;TKey&amp;gt;? comparer);&#13;
&#13;
        public static IQueryable&amp;lt;TSource&amp;gt; IntersectBy&amp;lt;TSource, TKey&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source1, IEnumerable&amp;lt;TSource&amp;gt; source2, Expression&amp;lt;Func&amp;lt;TSource, TKey&amp;gt;&amp;gt; keySelector);&#13;
        public static IQueryable&amp;lt;TSource&amp;gt; IntersectBy&amp;lt;TSource, TKey&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source1, IEnumerable&amp;lt;TSource&amp;gt; source2, Expression&amp;lt;Func&amp;lt;TSource, TKey&amp;gt;&amp;gt; keySelector, IEqualityComparer&amp;lt;TKey&amp;gt;? comparer);&#13;
        public static IQueryable&amp;lt;TSource&amp;gt; IntersectBy&amp;lt;TSource, TKey&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source1, IEnumerable&amp;lt;TKey&amp;gt; source2, Expression&amp;lt;Func&amp;lt;TSource, TKey&amp;gt;&amp;gt; keySelectorFirst);&#13;
        public static IQueryable&amp;lt;TSource&amp;gt; IntersectBy&amp;lt;TSource, TKey&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source1, IEnumerable&amp;lt;TKey&amp;gt; source2, Expression&amp;lt;Func&amp;lt;TSource, TKey&amp;gt;&amp;gt; keySelectorFirst, IEqualityComparer&amp;lt;TKey&amp;gt;? comparer);&#13;
&#13;
        public static IQueryable&amp;lt;TSource&amp;gt; UnionBy&amp;lt;TSource, TKey&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source1, IEnumerable&amp;lt;TSource&amp;gt; source2, Expression&amp;lt;Func&amp;lt;TSource, TKey&amp;gt;&amp;gt; keySelector);&#13;
        public static IQueryable&amp;lt;TSource&amp;gt; UnionBy&amp;lt;TSource, TKey&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source1, IEnumerable&amp;lt;TSource&amp;gt; source2, Expression&amp;lt;Func&amp;lt;TSource, TKey&amp;gt;&amp;gt; keySelector, IEqualityComparer&amp;lt;TKey&amp;gt;? comparer);&#13;
&#13;
        public static TSource MinBy&amp;lt;TSource, TResult&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source, Expression&amp;lt;Func&amp;lt;TSource, TResult&amp;gt;&amp;gt; selector);&#13;
        public static TSource MinBy&amp;lt;TSource, TResult&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source, Expression&amp;lt;Func&amp;lt;TSource, TResult&amp;gt;&amp;gt; selector, IComparer&amp;lt;TResult&amp;gt;? comparer);&#13;
&#13;
        public static TSource MaxBy&amp;lt;TSource, TResult&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source, Expression&amp;lt;Func&amp;lt;TSource, TResult&amp;gt;&amp;gt; selector);&#13;
        public static TSource MaxBy&amp;lt;TSource, TResult&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source, Expression&amp;lt;Func&amp;lt;TSource, TResult&amp;gt;&amp;gt; selector, IComparer&amp;lt;TResult&amp;gt;? comparer);&#13;
&#13;
        // Missing min &amp;amp; max overloads accepting custom comparers added for completeness&#13;
        public static TResult Min&amp;lt;TSource, TResult&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source, IComparer&amp;lt;TResult&amp;gt;? comparer);&#13;
        public static TResult Max&amp;lt;TSource, TResult&amp;gt;(this IQueryable&amp;lt;TSource&amp;gt; source, IComparer&amp;lt;TResult&amp;gt;? comparer);&#13;
    }&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;Bu API'ların neden geldiğini basitçe &lt;code&gt;Distinct&lt;/code&gt; API'ı üzerinden açıklarsak, &lt;code&gt;Distinct&lt;/code&gt; API'ını kullandığımızda bu API default equality comparer'ı kullanarak distinct elemanlı buluyor. Eğer complex bir type üzerinden bu metodu kullanmak istersek tipimizin &lt;code&gt;IEquatable&amp;lt;T&amp;gt;&lt;/code&gt; interface'ini implemente etmesi gerekmekte. &lt;code&gt;DistictBy&lt;/code&gt; metoduyla bu zorunluluğu ortadan kaldırıp metoda parametre olarak &lt;code&gt;keySelector&lt;/code&gt; geçebiliyoruz.&lt;/p&gt;&#13;
&lt;p&gt;Örnek yaparsak&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var list = new List&amp;lt;Product&amp;gt;();&#13;
var distinctProducts = list.DistinctBy(t =&amp;gt; t.SKU);&#13;
var cheapestProduct = list.MinBy(t =&amp;gt; t.Price);&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h2 id="chunk-metodu"&gt;Chunk Metodu&lt;/h2&gt;&#13;
&lt;p&gt;.NET 6 ile beraber gelen en beğendiğim API'lardan biri de &lt;code&gt;Chunk&lt;/code&gt; API'ı. Elimizde bulunan bir &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt; veya &lt;code&gt;IQueryable&amp;lt;T&amp;gt;&lt;/code&gt;'den belirttiğimiz uzunlukta chunklar oluşturabiliyoruz.&lt;/p&gt;&#13;
&lt;p&gt;Eklenen API'lar&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;namespace System.Linq&#13;
{&#13;
    public static class Enumerable&#13;
    {&#13;
        public static IEnumerable&amp;lt;T[]&amp;gt; Chunk(this IEnumerable&amp;lt;T&amp;gt; source, int size);&#13;
    }&#13;
    public static class Queryable&#13;
    {&#13;
        public static IQueryable&amp;lt;T[]&amp;gt; Chunk(this IQueryable&amp;lt;T&amp;gt; source, int size);&#13;
    }&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;Örnek yaparsak.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var list = new List&amp;lt;int&amp;gt;&#13;
{&#13;
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13&#13;
};&#13;
&#13;
IEnumerable&amp;lt;int[]&amp;gt;? chunks = list.Chunk(3);&#13;
/*&#13;
Chunks:&#13;
[1,2,3]&#13;
[4,5,6]&#13;
[7,8,9]&#13;
[10,11,12]&#13;
[13]&#13;
*/&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h2 id="zip-metoduyla-üç-farklı-sequencei-birleştirme-olanağı"&gt;Zip Metoduyla Üç Farklı Sequence'i Birleştirme Olanağı&lt;/h2&gt;&#13;
&lt;p&gt;.NET 6 ile beraber &lt;code&gt;Zip&amp;lt;T&amp;gt;&lt;/code&gt; metodunu kullanarak üç farklı sequence'ı birleştirebiliyoruz.&lt;/p&gt;&#13;
&lt;p&gt;Gelen yeni API'lar şu şekilde.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;namespace System.Linq&#13;
{&#13;
    public static class Enumerable&#13;
    {&#13;
        public static IEnumerable&amp;lt;(TFirst First, TSecond Second, TThird Third)&amp;gt; Zip&amp;lt;TFirst, TSecond, TThird&amp;gt;(this IEnumerable&amp;lt;TFirst&amp;gt; first, IEnumerable&amp;lt;TSecond&amp;gt; second, IEnumerable&amp;lt;TThird&amp;gt; third);&#13;
    }&#13;
&#13;
    public static class Queryable&#13;
    {&#13;
        public static IQueryable&amp;lt;(TFirst First, TSecond Second, TThird Third)&amp;gt; Zip&amp;lt;TFirst, TSecond, TThird&amp;gt;(this IQueryable&amp;lt;TFirst&amp;gt; source1, IEnumerable&amp;lt;TSecond&amp;gt; source2, IEnumerable&amp;lt;TThird&amp;gt; source3);&#13;
    }&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;h2 id="trygetnonenumeratedcount-metodu"&gt;&lt;code&gt;TryGetNonEnumeratedCount&lt;/code&gt; Metodu&lt;/h2&gt;&#13;
&lt;p&gt;&lt;code&gt;TryGetNonEnumeratedCount&lt;/code&gt; metodu elimizde bulunan sequence'in enumerate etmeye zorlamadan boyutunu almayı sağlıyor. Bu metot özellikle bir sequence'i enumerate etmeden önce buffer allocate etmek istersek önceden sequence'in büyüklüğünü bulup daha efektif bir şekilde buffer allocate etmemize yardımcı olabilir.&lt;/p&gt;&#13;
&lt;p&gt;Eklenen API&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;namespace System.Linq&#13;
{&#13;
    public static class Enumerable&#13;
    {&#13;
        public static bool TryGetNonEnumeratedCount(this IEnumerable&amp;lt;T&amp;gt; source, out int count);&#13;
    }&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;Örnek kullanım.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;List&amp;lt;T&amp;gt; buffer = source.TryGetNonEnumeratedCount(out int count) ? new List&amp;lt;T&amp;gt;(capacity: count) : new List&amp;lt;T&amp;gt;();&#13;
foreach (T item in source)&#13;
{&#13;
    buffer.Add(item);&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;Bu yazıda da .NET 6 ile beraber LINQ tarafında gelen yenilikleri inceledik.&lt;/p&gt;&#13;
&lt;p&gt;Bir sonraki yazıda görüşmek üzere.&lt;/p&gt;</description>
    </item>
    <item>
      <guid isPermaLink="false">dotnet-6-ile-beraber-basitlestirilmeye-calisilan-null-argument-kontrolu</guid>
      <link>https://www.ilkayilknur.com/dotnet-6-ile-beraber-basitlestirilmeye-calisilan-null-argument-kontrolu</link>
      <title>.NET 6 İle Basitleştirilmeye Çalışılan Null Argument Kontrolü</title>
      <description>&lt;p&gt;C# 10 ve .NET 6'nın en önemli temalarından biri yazdığımız kodu basitleştirmek ve azaltmak. Bu kapsamda gerek .NET içerisinde gerekse C# içerisinde pek çok yenilik geliyor. Bu yazıdaki konumuz ise metotlardaki null argument kontrolü. Build konferansında C#'ın yeni versiyonu ilk duyurulduğunda beni heyecanlandıran özelliklerden birisi basitleştirilmiş ve yükü compilerın üstlendiği null argument check özelliğiydi.&lt;/p&gt;&#13;
&lt;p&gt;Kısaca bir null argument kontrolü yaptığımız kod yazarsak.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public void Foo(Person person)&#13;
{&#13;
     if (person == null)&#13;
        throw new ArgumentNullException(nameof(person));&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;C# 10 ile bu yazdığımız kod aşağıdaki gibi olacaktı.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public void Foo(Person person!)&#13;
{&#13;
    &#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;Ancak bu özellik ne yazık ki C# 10 ile beraber gelemeyecek. Bununla beraber .NET 6 ile yazdığımız kodu biraz daha kısaltacak yeni bir metot geliyor. Bu metot &lt;code&gt;ArgumentNullException.ThrowIfNull&lt;/code&gt; metodu. Bu metodu kullanarak en azından ekstra if statementlarından kurtulabiliriz.&lt;/p&gt;&#13;
&lt;p&gt;Yeni gelen metotla beraber yukarıdaki kod aşağıdaki gibi olacak.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public void Foo(Person person)&#13;
{&#13;
    ArgumentNullException.ThrowIfNull(person);&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;&lt;code&gt;ThrowIfNull&lt;/code&gt; metodunun .NET içerisindeki implementasyonu ise aşağıdaki gibi.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpression(&amp;quot;argument&amp;quot;)] string? paramName = null)&#13;
{&#13;
   if (argument is null)&#13;
   {&#13;
       Throw(paramName);&#13;
   }&#13;
}&#13;
&#13;
[DoesNotReturn]&#13;
private static void Throw(string? paramName) =&amp;gt;&#13;
   throw new ArgumentNullException(paramName);&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;&lt;a href="https://source.dot.net/#System.Private.CoreLib/ArgumentNullException.cs,c742289497493bb8" target="_blank"&gt;https://source.dot.net/#System.Private.CoreLib/ArgumentNullException.cs,c742289497493bb8&lt;/a&gt;&lt;/p&gt;&#13;
&lt;p&gt;Yukarıda gördüğünüz &lt;code&gt;CallerArgumentExpression&lt;/code&gt; attribute'ü size yeni gelebilir. Bu da bir başka makalenin konusu olsun. &#128579;&lt;/p&gt;&#13;
&lt;p&gt;Bir sonraki yazıda görüşmek üzere.&lt;/p&gt;</description>
    </item>
    <item>
      <guid isPermaLink="false">csharp-10-global-usings-ve-file-scoped-namespaces-ozellikleri</guid>
      <link>https://www.ilkayilknur.com/csharp-10-global-usings-ve-file-scoped-namespaces-ozellikleri</link>
      <title>C# 10 Global Usings ve File Scoped Namespaces Özellikleri</title>
      <description>&lt;p&gt;.NET 6'nın ilk release candidate sürümünün çıkmasıyla beraber gerek framework içerisindeki özelliklerin gerekse C# 10 içerisindeki özelliklerin daha stabil ve değişikliğe uğramasının da düşük bir olasılıkta olduğunu düşünebiliriz. Bu nedenle de artık yavaş yavaş C# 10 içerisindeki özellikleri incelemenin de vaktinin geldiğini düşünüyorum.&lt;/p&gt;&#13;
&lt;p&gt;Bu yazıda C# 10 içerisinde bulunan ve adapte olması en kolay olan özelliklerden bahsedeceğiz. Vakit kaybetmeden incelemeye başlayalım.&lt;/p&gt;&#13;
&lt;h2 id="global-usings"&gt;Global Usings&lt;/h2&gt;&#13;
&lt;p&gt;Bu özellik kısaca bir proje içerisinde çalışırken sıklıkla kullandığımız namespaceleri global olarak tanımlamamızı sağlıyor. Global olarak tanımladığımız namespaceleri proje içerisindeki tüm dosyalar içerisinden otomatik olarak kullanabiliyoruz.&lt;/p&gt;&#13;
&lt;p&gt;Kısaca bir örnek verirsek, diyelim ki projemizde Entity Framework kullanıyoruz ve proje içerisindeki tüm dosyalar içerisinde &lt;code&gt;using Microsoft.EntityFrameworkCore;&lt;/code&gt; şeklinde bir tanımlama yapıyoruz. Bu kod kalabalığından ve tekrardan kurtulmak için &lt;code&gt;using&lt;/code&gt; statementının başına &lt;code&gt;global&lt;/code&gt; keywordünü ekleyerek bu namespace'in tüm dosyalarda otomatik importlu şekilde gelmesini sağlayabiliriz.&lt;/p&gt;&#13;
&lt;p&gt;Global usings özelliğinin kullanımında tavsiye edilen yöntem her proje başına bir &lt;code&gt;using.cs&lt;/code&gt; gibi bir dosya tanımlanması ve global olarak import edilecek olan namespacelerin bu dosya içerisinden eklenmesi ve yönetilmesi. Aksi takdirde farklı dosyalar içerisinde global tanımlamalar yapıldığında namespacelerin yönetimi oldukça zorlaşabilir.&lt;/p&gt;&#13;
&lt;p&gt;.NET 6 ile beraber gelen dikkat çekici yeniliklerden biri de uygulama templatelerinin güncelleniyor olması. Bu güncelleme sonrasında farklı uygulama tiplerine göre sık kullanılan bazı namespaceler otomatik olarak global bir şekilde import edilmiş olarak geliyor. SDK tipine göre global import edilen namespaceleri aşağıda görebilirsiniz.&lt;/p&gt;&#13;
&lt;ul&gt;&#13;
&lt;li&gt;&lt;p&gt;Microsoft.NET.SDK:&lt;/p&gt;&#13;
&lt;ul&gt;&#13;
&lt;li&gt;System&lt;/li&gt;&#13;
&lt;li&gt;System.Collections.Generic&lt;/li&gt;&#13;
&lt;li&gt;System.IO&lt;/li&gt;&#13;
&lt;li&gt;System.Linq&lt;/li&gt;&#13;
&lt;li&gt;System.Net.Http&lt;/li&gt;&#13;
&lt;li&gt;System.Threading&lt;/li&gt;&#13;
&lt;li&gt;System.Threading.Tasks&lt;/li&gt;&#13;
&lt;/ul&gt;&#13;
&lt;/li&gt;&#13;
&lt;li&gt;&lt;p&gt;Microsoft.NET.SDK.Web:&lt;/p&gt;&#13;
&lt;ul&gt;&#13;
&lt;li&gt;&lt;em&gt;Microsoft.NET.SDK içerisinde ekli olanlar ve aşağıdaki namespaceler.&lt;/em&gt;&lt;/li&gt;&#13;
&lt;li&gt;System.Net.Http.Json&lt;/li&gt;&#13;
&lt;li&gt;Microsoft.AspNetCore.Builder&lt;/li&gt;&#13;
&lt;li&gt;Microsoft.AspNetCore.Hosting&lt;/li&gt;&#13;
&lt;li&gt;Microsoft.AspNetCore.Http&lt;/li&gt;&#13;
&lt;li&gt;Microsoft.AspNetCore.Routing&lt;/li&gt;&#13;
&lt;li&gt;Microsoft.Extensions.Configuration&lt;/li&gt;&#13;
&lt;li&gt;Microsoft.Extensions.DependencyInjection&lt;/li&gt;&#13;
&lt;li&gt;Microsoft.Extensions.Hosting&lt;/li&gt;&#13;
&lt;li&gt;Microsoft.Extensions.Logging&lt;/li&gt;&#13;
&lt;/ul&gt;&#13;
&lt;/li&gt;&#13;
&lt;li&gt;&lt;p&gt;Microsoft.NET.SDK.Worker&lt;/p&gt;&#13;
&lt;ul&gt;&#13;
&lt;li&gt;&lt;em&gt;Microsoft.NET.SDK içerisinde ekli olanlar ve aşağıdaki namespaceler.&lt;/em&gt;&lt;/li&gt;&#13;
&lt;li&gt;Microsoft.Extensions.Configuration&lt;/li&gt;&#13;
&lt;li&gt;Microsoft.Extensions.DependencyInjection&lt;/li&gt;&#13;
&lt;li&gt;Microsoft.Extensions.Hosting&lt;/li&gt;&#13;
&lt;li&gt;Microsoft.Extensions.Logging&lt;/li&gt;&#13;
&lt;/ul&gt;&#13;
&lt;/li&gt;&#13;
&lt;/ul&gt;&#13;
&lt;p&gt;Bu otomatik import özelliğinde hoşlanmadıysanız veya global olarak eklenecek olan namespaceleri kendiniz kontrol etmek isterseniz proje dosyası içerisine &lt;code&gt;&amp;lt;ImplicitUsings&amp;gt;disable&amp;lt;/ImplicitUsings&amp;gt;&lt;/code&gt; ekleyebilirsiniz.&lt;/p&gt;&#13;
&lt;h2 id="file-scoped-namespaces"&gt;File Scoped Namespaces&lt;/h2&gt;&#13;
&lt;p&gt;C# 10 içerisinde belki de en sevilecek özelliklerden biri olmaya aday olan bir özellik file scoped namespaces. Bu özelliği uzun uzun anlatmaya gerek yok. En basit anlatımla namespace tanımlamalarını kısaltabiliyoruz.&lt;/p&gt;&#13;
&lt;p&gt;C# 10 öncesi&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;namespace MyProduct&#13;
{&#13;
    class X&#13;
    {&#13;
&#13;
    }&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;C# 10 ile beraber&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;namespace MyProduct;&#13;
class X&#13;
{&#13;
&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;Görüldüğü gibi file-scoped namespaceleri kullanarak ekstra curly brace ve indentationdan kurtulmuş oluyoruz. Bu özelliğin tabi ki bazı limitasyonları bulunmakta. Özelliğin adından da anlayabileceğiniz üzere tanımladığımız namespace dosya bazında geçerli. Bu nedenle birden fazla namespace tanımlaması yapmamız mümkün değil. Aynı zamanda bir dosya içerisinde hem file scoped hem de normal namespace kullanımı da yapamıyoruz. Ancak çoğu dosyada zaten bunlara pek ihtiyacımız olmayacağı için bence oldukça iyi bir özellik olduğunu söyleyebilirim.&lt;/p&gt;&#13;
&lt;p&gt;Bu yazıda C# 10 ile beraber gelecek olan nispeten daha basit özellikleri inceledik.&lt;/p&gt;&#13;
&lt;p&gt;Bir sonraki yazıda görüşmek üzere.&lt;/p&gt;</description>
    </item>
    <item>
      <guid isPermaLink="false">dotnet-6-ile-beraber-gelen-yeni-bir-timer-periodictimer</guid>
      <link>https://www.ilkayilknur.com/dotnet-6-ile-beraber-gelen-yeni-bir-timer-periodictimer</link>
      <title>.NET 6 İle Beraber Gelen Yeni Bir Timer Tipi - PeriodicTimer</title>
      <description>&lt;p&gt;.NET içerisinde pek çok alternatifini bulabileceğimiz yapılardan biri de &lt;code&gt;Timer&lt;/code&gt; yapıları. Geçmişten bugüne kadar farklı farklı noktalarda farklı ihtiyaçlara cevap veren timer yapıları framework içerisine eklendi ve aktif olarak kullanılmakta.&lt;/p&gt;&#13;
&lt;p&gt;.NET 6 öncesinde kullanabileceğimiz timer tiplerini kısaca listelersek...&lt;/p&gt;&#13;
&lt;ul&gt;&#13;
&lt;li&gt;&lt;code&gt;System.Threading.Timer&lt;/code&gt;&lt;/li&gt;&#13;
&lt;li&gt;&lt;code&gt;System.Timers.Timer&lt;/code&gt;&lt;/li&gt;&#13;
&lt;li&gt;&lt;code&gt;System.Windows.Forms.Timer&lt;/code&gt;&lt;/li&gt;&#13;
&lt;li&gt;&lt;code&gt;System.Web.UI.Timer&lt;/code&gt;&lt;/li&gt;&#13;
&lt;li&gt;&lt;code&gt;System.Windows.Threading.DispatcherTimer&lt;/code&gt;&lt;/li&gt;&#13;
&lt;/ul&gt;&#13;
&lt;p&gt;.NET 6 ile beraber ise bu listeye bir timer tipini daha ekliyor olacağız. .NET 6 Preview 7 ile beraber bu listeye dahil olan timer tipinin adı &lt;code&gt;PeriodicTimer&lt;/code&gt;. &lt;code&gt;PeriodicTimer&lt;/code&gt; da tabi ki mevcut olan timer tiplerin içerisindeki bir takım problemleri çözmek amacıyla framework içerisine eklendi. Mevcut timer tiplerinde sorun yaratan konuların başında bu timer tiplerinin callback tabanlı çalışması yatmaktaydı. Bu callback yapıları bazen memory leaklere neden olabilmekte ve sorun çıkarabilmekte. Aynı zamanda bu callbackler içerisinde &lt;code&gt;sync over async&lt;/code&gt; kod yazmamız gerekmekte. Mevcut timer tiplerinde timer ticklendiğinde çalışacak olan kodlar eğer çalışma süreleri uzun sürerse arka arkaya tetiklenebilmekte ve eğer kodumuzu buna göre yazmazsak sorunlara neden olabilmekte.&lt;/p&gt;&#13;
&lt;p&gt;Tüm bu nedenlerden dolayı .NET 6 ile beraber kullanımı oldukça basit bir timer yapısı hayatımıza giriyor. Şimdi kod kısmına geçelim.&lt;/p&gt;&#13;
&lt;p&gt;Bir &lt;code&gt;PeriodicTimer&lt;/code&gt; instance'ı yaratmak oldukça basit. Sadece constructora ne kadar sürede bir tetikleneceğini bildirmemiz gerekmekte.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var timer = new PeriodicTimer(TimeSpan.FromSeconds(3));&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;Sonrasında da &lt;code&gt;WaitForNextTickAsync&lt;/code&gt; metodunu çağırarak bir sonraki tetiklenmeyi async olarak bekleyebilir, ardındanda çalışacak olan kodu çalıştırabiliriz.&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var timer = new PeriodicTimer(TimeSpan.FromSeconds(3));&#13;
&#13;
while (await timer.WaitForNextTickAsync())&#13;
{&#13;
    Console.WriteLine(&amp;quot;Timer tick&amp;quot;);&#13;
}&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;&lt;code&gt;WaitForNextTickAsync&lt;/code&gt; metodunu tekrar çağırmadığımız sürece timer tekrardan verilen periyot kadar bekleme operasyonunu başlatmadığı için yazdığımız kodun üst üste çalışması gibi bir durum da olmamakta.&lt;/p&gt;&#13;
&lt;p&gt;Şimdi gelelim &lt;code&gt;PeriodicTimer&lt;/code&gt;'ın durdurulması senaryosuna. Bunun için iki farklı yöntem bulunmakta. Bunlardan biri &lt;code&gt;WaitForNextTickAsync&lt;/code&gt; metoduna bir &lt;code&gt;CancellationToken&lt;/code&gt; verilmesi. Diğeri ise &lt;code&gt;PeriodicTimer&lt;/code&gt;'ın dispose metodunun çağırılması. &lt;code&gt;CancellationToken&lt;/code&gt; kullanımında eğer operasyon iptal edilirse  exception fırlatılırken, dispose etme senaryosunda &lt;code&gt;WaitForNextTickAsync&lt;/code&gt; metodundan &lt;code&gt;false&lt;/code&gt; dönmekte.&lt;/p&gt;&#13;
&lt;p&gt;Bu yazıda .NET 6 ile beraber gelen &lt;code&gt;PeriodicTimer&lt;/code&gt;'ı inceledik. Bir sonraki yazıda görüşmek üzere.&lt;/p&gt;&#13;
&lt;p&gt;Kaynak : &lt;a href="https://github.com/dotnet/runtime/issues/31525" target="_blank"&gt;https://github.com/dotnet/runtime/issues/31525&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <guid isPermaLink="false">random-shared-propertysi-ile-random-tipinin-thread-safe-olarak-kullanilmasi</guid>
      <link>https://www.ilkayilknur.com/random-shared-propertysi-ile-random-tipinin-thread-safe-olarak-kullanilmasi</link>
      <title>Random.Shared Propertysi İle Random Tipinin Thread-Safe Olarak Kullanılması</title>
      <description>&lt;p&gt;Developerlar olarak sıklıkla kullandığımız tiplerden biri de &lt;code&gt;Random&lt;/code&gt; tipi. Bazen test amaçlı olarak bazen de farklı amaçlarla &lt;code&gt;Random&lt;/code&gt; tipine sıklıkla başvuruyoruz. &lt;code&gt;Random&lt;/code&gt; tipinin özelliklerinden biri de thread-safe olmaması. Bir &lt;code&gt;Random&lt;/code&gt; instance'ının concurrent olarak kullanılması nadiren de olsa üretilen sayıların &lt;strong&gt;0&lt;/strong&gt; olmasına bazen de randomizasyonun kalitesinin düşmesine neden olabilmekte.&lt;/p&gt;&#13;
&lt;p&gt;Bu noktada kullanabileceğimiz çözümler &lt;code&gt;lock&lt;/code&gt; kullanmak veya her bir thread içerisinde &lt;code&gt;ThreadStatic&lt;/code&gt; field içerisinde farklı bir &lt;code&gt;Random&lt;/code&gt; instance'ı kullanmak olabilir. Ancak her iki durumda da developerlar &lt;code&gt;Random&lt;/code&gt; tipini thread-safe bir şekilde kullanmak için ekstra kod yazmak durumunda kalmakta. Bu senaryo .NET 6 ile beraber biraz değişiyor. .NET 6 ile beraber gelen &lt;code&gt;Random.Shared&lt;/code&gt; propertysini kullanarak thread-safe olarak &lt;code&gt;Random&lt;/code&gt; tipini kullanabiliyoruz.&lt;/p&gt;&#13;
&lt;p&gt;Örnek olarak&lt;/p&gt;&#13;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var x = Random.Shared.Next();&#13;
&lt;/code&gt;&lt;/pre&gt;&#13;
&lt;p&gt;Bu şekilde kullanımda arkada kullanılan implementasyon &lt;code&gt;ThreadStatic&lt;/code&gt; field içerisinde &lt;a href="https://source.dot.net/#System.Private.CoreLib/Random.cs,235" target="_blank"&gt;saklandığı&lt;/a&gt; için concurrent kullanımlarda herhangi bir sorunla karşılaşmıyoruz ve aynı zamanda kolay ve basit bir şekilde thread-safe implementasyona da sahip olmuş oluyoruz.&lt;/p&gt;&#13;
&lt;p&gt;Bir sonraki yazıda görüşmek üzere.&lt;/p&gt;&#13;
&lt;p&gt;Kaynak: &lt;a href="https://github.com/dotnet/runtime/issues/43887" target="_blank"&gt;https://github.com/dotnet/runtime/issues/43887&lt;/a&gt;&lt;/p&gt;</description>
    </item>
  </channel>
</rss>