<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Grant Riordan</title>
    <description>The latest articles on DEV Community by Grant Riordan (@grantdotdev).</description>
    <link>https://dev.to/grantdotdev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F434380%2F4f092a26-2eda-4d8b-8a10-2f71109596ac.png</url>
      <title>DEV Community: Grant Riordan</title>
      <link>https://dev.to/grantdotdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/grantdotdev"/>
    <language>en</language>
    <item>
      <title>AI Can Not Be Left Unsupervised</title>
      <dc:creator>Grant Riordan</dc:creator>
      <pubDate>Thu, 26 Mar 2026 23:42:34 +0000</pubDate>
      <link>https://dev.to/grantdotdev/ai-can-not-be-left-unsupervised-5ad</link>
      <guid>https://dev.to/grantdotdev/ai-can-not-be-left-unsupervised-5ad</guid>
      <description>&lt;p&gt;"AI is dangerous" — is a phrase you may hear a lot, and I have to agree. But, the most dangerous words in tech right now aren't "&lt;em&gt;AI is dangerous&lt;/em&gt;", they're "&lt;em&gt;it'll probably be fine&lt;/em&gt;". In a society where the use of AI has become the norm, people have become almost complacent around the dangers of it.&lt;/p&gt;

&lt;p&gt;This week I read an article on Dev.to &lt;a href="https://dev.to/harsh2644/agentic-ai-is-the-most-overhyped-thing-in-tech-and-i-have-proof-1785"&gt;here&lt;/a&gt;, telling a story where an employee had "trusted AI to organize my backlog." Two hours later, they returned to a development team's worst nightmare.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The agent had silently deleted 47 tickets it labelled as duplicates — they weren't. It had reassigned half my team's tasks to people who had left the company months ago. It created 23 new tickets for features nobody had requested. And it marked three critical bugs as resolved, because it found similar-sounding issues elsewhere in the system."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A prime example of when AI is left to its own devices — no warning, no follow-up prompts. It was asked to organise a backlog, and so it did, in the only way it knew how.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Damaging Cases
&lt;/h2&gt;

&lt;p&gt;This isn't an isolated incident. Amazon's own AI coding tool, Kiro, was handed a minor fix to a customer-facing system. Rather than patch it, the agent autonomously decided to delete and rebuild the entire environment from scratch. The resulting outage lasted 13 hours. Amazon's response? "Coincidence that AI was involved." That denial tells you everything. &lt;/p&gt;

&lt;p&gt;In a similar case, an AI coding assistant from &lt;strong&gt;Replit&lt;/strong&gt; went rogue and wiped the production database of startup &lt;strong&gt;SaaStr&lt;/strong&gt; entirely — its founder took to social media to warn others after the assistant modified production code despite explicit instructions not to.&lt;/p&gt;

&lt;p&gt;Could your business afford that kind of downtime?&lt;/p&gt;

&lt;p&gt;It isn't just agentic AI operating behind the scenes. Customer-facing AI is failing in equally public and embarrassing ways. DPD's AI chatbot, after a routine system update, began swearing at a customer, insulted itself, and wrote a poem about how terrible the company was, all because a frustrated user simply asked it to. The incident went viral. &lt;/p&gt;

&lt;p&gt;Perhaps the most alarming example of AI complacency comes from a Chevrolet dealership, whose chatbot agreed to sell a 2024 Chevy Tahoe for &lt;strong&gt;$1&lt;/strong&gt; and declared it a legally binding offer after a user simply manipulated it with a clever prompt. The dealer pulled the bot, but the damage to their brand was already done.&lt;/p&gt;

&lt;p&gt;In August 2025, security researchers used a single 400-character prompt to manipulate Lenovo's customer service chatbot into revealing sensitive company data including live session cookies from real support agents. Not a sophisticated hack. Just a simple prompt.&lt;/p&gt;

&lt;p&gt;Outside of customer service, the professional world is sleepwalking into its own risks. A lawyer was caught citing entirely non-existent legal cases in a New York federal court filing he had used ChatGPT to conduct legal research, and &lt;strong&gt;simply trusted the output&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;With the rapid adoption of AI across industries, has the world become complacent, siding with productivity over pragmatism? More often than not, users are blindly allowing AI to complete work for them, review sensitive documents outside the bounds of a secure environment, and make decisions without a single human checkpoint. &lt;/p&gt;

&lt;p&gt;I know many developers who simply click "Accept" when prompted to approve terminal commands. I've spoken to professionals within my network who say "AI has evolved, it can be trusted." That ignorance and naivety is precisely what leads to the scenarios above.&lt;/p&gt;

&lt;p&gt;The companies and individuals that will thrive in an AI-driven world aren't the ones adopting it the fastest. They're the ones adopting it the most responsibly. &lt;/p&gt;

&lt;h2&gt;
  
  
  Final Note
&lt;/h2&gt;

&lt;p&gt;Before you click "Accept" on that next AI suggestion, ask yourself honestly: do you actually know what you're agreeing to? Don't simply outsource your thinking to something that doesn't think - it predicts. &lt;/p&gt;

&lt;p&gt;AI isn't the danger. We are.&lt;/p&gt;

&lt;p&gt;I’d love to hear your thoughts - comment below or drop me a follow on &lt;a href="https://x.com/grantdotdev" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
    </item>
    <item>
      <title>Using Python To Merge Array of Ranges</title>
      <dc:creator>Grant Riordan</dc:creator>
      <pubDate>Tue, 30 Dec 2025 22:16:48 +0000</pubDate>
      <link>https://dev.to/grantdotdev/using-python-to-merge-array-of-ranges-2hic</link>
      <guid>https://dev.to/grantdotdev/using-python-to-merge-array-of-ranges-2hic</guid>
      <description>&lt;p&gt;So I was doing an Advent of Code 2025 challenge the other day, and had an issue where I needed to count all the unique numbers between multiple ranges. It reminded me of some code I'd written previously for a booking system.&lt;/p&gt;

&lt;p&gt;The problem was that the ranges overlapped in many cases. Now, looping over all the numbers and using a &lt;code&gt;set()&lt;/code&gt; was not feasible, nor performant as the ranges could be in the millions. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Naïve Approach
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;unique_values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ranges&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;unique_values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a range of say &lt;code&gt;1-100_000_000_000&lt;/code&gt; you'd create 1 billion numbers in memory, that's huge! You're also materialising data that doesn't need to exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution - Merge First, Count Later
&lt;/h2&gt;

&lt;p&gt;Instead of creating every number in memory (expensive), we merge overlapping ranges into fewer, larger ranges, then calculate the count mathematically.&lt;/p&gt;

&lt;p&gt;Lets look at a smaller range for the purpose of this tutorial:&lt;/p&gt;

&lt;p&gt;Let's say we have the following ranges:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;some of them overlap or touch each other. &lt;br&gt;
&lt;code&gt;10-14&lt;/code&gt; and &lt;code&gt;12-18&lt;/code&gt; overlap (they share 12, 13, 14)&lt;/p&gt;

&lt;p&gt;If merged, they become one range: 10-18. Without merging, you'd count numbers twice or waste memory storing every individual number.&lt;/p&gt;
&lt;h2&gt;
  
  
  How To Merge Overlapping Ranges
&lt;/h2&gt;
&lt;h3&gt;
  
  
  First Thing - Let's Get The Ranges Sorted
&lt;/h3&gt;

&lt;p&gt;Sorting the arrays by their start position makes the merging a lot easier - we read from left-to-right on the number line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;sorted_ranges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fresh_ranges&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Before: [(3,5), (10,14), (16,20), (12,18)]
# After:  [(3,5), (10,14), (12,18), (16,20)]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How does this help though? When you encounter a new range, you only need to check if it overlaps with the last merged range. There's no need to check all previous ranges. If ranges are sorted, overlaps always happen with the most recent range, saving time and effort checking other ranges.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Sorted number line view:
   3---5      10----14
              12------18
                  16----20

&lt;span class="c"&gt;# You can see overlaps flow left-to-right!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Second - Merge the overlapping ranges
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;merged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sorted_ranges&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;merged&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Overlaps or touches - merge!
&lt;/span&gt;        &lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Doesn't overlap - add as new range
&lt;/span&gt;        &lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why &lt;code&gt;merged and start &amp;lt;= merged[-1][1] + 1&lt;/code&gt;?&lt;br&gt;
This condition checks two things:&lt;/p&gt;

&lt;p&gt;merged → Is there at least one range already merged?&lt;/p&gt;

&lt;p&gt;First iteration: merged = [] → False, so add first range&lt;/p&gt;

&lt;p&gt;Later: merged = [(3,5)] → True, check overlap&lt;br&gt;
start &amp;lt;= merged[-1][1] + 1 → Does current range touch/overlap the last merged range?&lt;/p&gt;
&lt;h4&gt;
  
  
  Lets take a look at some examples:
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Case 1: Adjacent (touching)
&lt;/span&gt;&lt;span class="n"&gt;Last&lt;/span&gt; &lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# ends at 14
&lt;/span&gt;&lt;span class="n"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# starts at 15
&lt;/span&gt;&lt;span class="n"&gt;Check&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;YES&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt; &lt;span class="n"&gt;They&lt;/span&gt; &lt;span class="n"&gt;touch&lt;/span&gt;

&lt;span class="c1"&gt;# Case 2: Gap between ranges
&lt;/span&gt;&lt;span class="n"&gt;Last&lt;/span&gt; &lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# ends at 5
&lt;/span&gt;&lt;span class="n"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# starts at 10
&lt;/span&gt;&lt;span class="n"&gt;Check&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;NO&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt; &lt;span class="n"&gt;Gap&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Why + 1?&lt;/p&gt;

&lt;p&gt;Ranges for example like (5,10) and (11,15) are adjacent. They shouldn't stay separate (gap at position 10.5). The &lt;code&gt;+ 1&lt;/code&gt; is about merging adjacent ranges that touch, not just overlapping ones.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Range A: &lt;span class="o"&gt;(&lt;/span&gt;5, 9&lt;span class="o"&gt;)&lt;/span&gt;   →  5, 6, 7, 8, 9
Range B: &lt;span class="o"&gt;(&lt;/span&gt;10, 15&lt;span class="o"&gt;)&lt;/span&gt; →  10, 11, 12, 13, 14, 15

Number line:
5--6--7--8--9--10--11--12--13--14--15
&lt;span class="o"&gt;[======&lt;/span&gt;&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;======][========&lt;/span&gt;&lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;========]&lt;/span&gt;
              ↑
          They TOUCH at position 9→10

Without +1:
Check: 10 &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; 9? → NO ❌
Result: Keep separate &lt;span class="o"&gt;[(&lt;/span&gt;5,9&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="o"&gt;(&lt;/span&gt;10,15&lt;span class="o"&gt;)]&lt;/span&gt;
This is WRONG - there is no gap! They should be one range: &lt;span class="o"&gt;(&lt;/span&gt;5,15&lt;span class="o"&gt;)&lt;/span&gt;

With +1:
Check: 10 &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; 9+1? → 10 &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; 10? → YES ✅
Result: Merge to &lt;span class="o"&gt;(&lt;/span&gt;5, 15&lt;span class="o"&gt;)&lt;/span&gt;
This is CORRECT!

&lt;span class="c"&gt;# Actual Gap&lt;/span&gt;
Range A: &lt;span class="o"&gt;(&lt;/span&gt;5, 9&lt;span class="o"&gt;)&lt;/span&gt;   →  5, 6, 7, 8, 9
Range B: &lt;span class="o"&gt;(&lt;/span&gt;11, 15&lt;span class="o"&gt;)&lt;/span&gt; →  11, 12, 13, 14, 15

Number line:
5--6--7--8--9--&lt;span class="o"&gt;(&lt;/span&gt;10&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;--11--12--13--14--15&lt;/span&gt;
&lt;span class="o"&gt;[======&lt;/span&gt;&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="o"&gt;======]&lt;/span&gt;  GAP  &lt;span class="o"&gt;[=======&lt;/span&gt;&lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;=======]&lt;/span&gt;
              ↑      ↑
          Ends at 9  Starts at 11
          10 is missing!

With +1:
Check: 11 &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; 9+1? → 11 &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; 10? → NO ✅
Result: Keep separate &lt;span class="o"&gt;[(&lt;/span&gt;5,9&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="o"&gt;(&lt;/span&gt;11,15&lt;span class="o"&gt;)]&lt;/span&gt;
This is CORRECT - there&lt;span class="s1"&gt;'s a gap at position 10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simply, sum each range to get the total number of items within the ranges.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;total_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total_count&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Final Code:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="c1"&gt;# Sort ranges by start position
&lt;/span&gt;    &lt;span class="n"&gt;sorted_ranges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fresh_ranges&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Merge overlapping ranges
&lt;/span&gt;    &lt;span class="n"&gt;merged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sorted_ranges&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;merged&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Overlapping or adjacent - merge with last range
&lt;/span&gt;            &lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# No overlap - add new range
&lt;/span&gt;            &lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;# Calculate total count from merged ranges
&lt;/span&gt;    &lt;span class="n"&gt;total_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total_count&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real Life Usages:
&lt;/h2&gt;

&lt;p&gt;There are many scenarios in real life where you may need to handle multiple ranges needing merged, or to be counted;&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;h3&gt;
  
  
  Appointment Booking Systems:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Meeting A: 9:00 AM - 10:30 AM
Meeting B: 10:15 AM - 11:00 AM  
Meeting C: 2:00 PM - 3:30 PM
Meeting D: 3:30 PM - 5:00 PM

Sorted: &lt;span class="o"&gt;[(&lt;/span&gt;9:00, 10:30&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="o"&gt;(&lt;/span&gt;10:15, 11:00&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="o"&gt;(&lt;/span&gt;2:00, 3:30&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="o"&gt;(&lt;/span&gt;3:30, 5:00&lt;span class="o"&gt;)]&lt;/span&gt;

Merged: &lt;span class="o"&gt;[(&lt;/span&gt;9:00, 11:00&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="o"&gt;(&lt;/span&gt;2:00, 5:00&lt;span class="o"&gt;)]&lt;/span&gt;
         ↑               ↑
   Meetings A+B overlap  Meetings C+D &lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;adjacent&lt;span class="o"&gt;)&lt;/span&gt;

Free &lt;span class="nb"&gt;times&lt;/span&gt;: Before 9:00, 11:00-2:00, After 5:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;When is the room actually free?&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  IP Address Blocking
&lt;/h3&gt;

&lt;p&gt;Block: 192.168.1.0 - 192.168.1.50&lt;br&gt;
Block: 192.168.1.45 - 192.168.1.100&lt;br&gt;
Block: 192.168.1.200 - 192.168.1.255&lt;/p&gt;

&lt;p&gt;Without merging, check 3 rules for every connection.&lt;br&gt;
With merging - &lt;br&gt;
[(192.168.1.0, 192.168.1.100), (192.168.1.200, 192.168.1.255)]&lt;/p&gt;

&lt;p&gt;Only 2 rules to check = Faster firewall!&lt;/p&gt;
&lt;h3&gt;
  
  
  Maintenance Downtime
&lt;/h3&gt;

&lt;p&gt;Server or Machine scheduled for maintenance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Maintenance A: Hour 0-5
Maintenance B: Hour 4-8
Maintenance C: Hour 20-24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Merged downtime: [(0, 8), (20, 24)]&lt;br&gt;
Total downtime: 8 + 4 = 12 hours (not 17 if counted individually)&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters in All Cases:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Avoid double-counting overlapping periods&lt;/li&gt;
&lt;li&gt;Optimise checks - fewer ranges to search&lt;/li&gt;
&lt;li&gt;Find gaps - available time slots&lt;/li&gt;
&lt;li&gt;Accurate reporting - real busy vs. free time&lt;/li&gt;
&lt;li&gt;Performance - checking fewer merged ranges vs. many overlapping ones&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The above pattern / method is used by multiple companies around the globe, handling calendar invites, database indexing, network traffic analysis and much more. &lt;/p&gt;

&lt;p&gt;Go ahead, use the above code to increase your applications performance. &lt;/p&gt;

&lt;p&gt;If you want to chat, or reach out don't hesitate to comment below, or  drop me a follow on &lt;a href="https://x.com/grantdotdev" rel="noopener noreferrer"&gt;x/twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
    </item>
    <item>
      <title>Spread Operator and Chaining in JS/TS</title>
      <dc:creator>Grant Riordan</dc:creator>
      <pubDate>Tue, 30 Sep 2025 13:39:09 +0000</pubDate>
      <link>https://dev.to/grantdotdev/spread-operator-and-chaining-in-jsts-1227</link>
      <guid>https://dev.to/grantdotdev/spread-operator-and-chaining-in-jsts-1227</guid>
      <description>&lt;p&gt;Hey, so I’m sometimes asked how the spread operator works in JS especially when working with chained statements. &lt;/p&gt;

&lt;p&gt;Some confuse the &lt;code&gt;...&lt;/code&gt; (spread) operator as applying to the first element of the chain, spreading the variable before applying chained functions.&lt;/p&gt;

&lt;p&gt;This isn’t correct; it applies to the result of the chain.&lt;/p&gt;

&lt;p&gt;Take this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getLongestWordLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;sentence&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some may think it spreads the &lt;code&gt;sentence&lt;/code&gt; variable first then applies the other chained functions, which you would be right in thinking would throw an error. &lt;/p&gt;

&lt;p&gt;However, due to the way that JS works the &lt;code&gt;.&lt;/code&gt; operator is seen as what's called a primary expression, meaning it takes *&lt;em&gt;precedence *&lt;/em&gt; over every other operator. &lt;/p&gt;

&lt;p&gt;This would be the same as if you wrote your code like this, wrapping your statement in brackets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getLongestWordLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;sentence&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But fortunately for us these extra brackets aren't needed due to the &lt;code&gt;.&lt;/code&gt; expression precedence. &lt;/p&gt;

&lt;p&gt;So in fact what the code is doing is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Replace all &lt;code&gt;.&lt;/code&gt; in a string.&lt;/li&gt;
&lt;li&gt;Split the result on spaces.&lt;/li&gt;
&lt;li&gt;Return the length of each word.&lt;/li&gt;
&lt;li&gt;Spread the lengths array to Math.max()&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Code is read left to right, treating the chain as a single variable / element - so no need to wrap our statement in extra brackets. &lt;/p&gt;

&lt;p&gt;Hope this helps 😁 - did you know this?&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>learning</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>C# LeetCode 268: Missing Number - (Easy)</title>
      <dc:creator>Grant Riordan</dc:creator>
      <pubDate>Thu, 25 Sep 2025 20:46:18 +0000</pubDate>
      <link>https://dev.to/grantdotdev/c-leetcode-268-missing-number-easy-2ogc</link>
      <guid>https://dev.to/grantdotdev/c-leetcode-268-missing-number-easy-2ogc</guid>
      <description>&lt;p&gt;LeetCode 268: Missing Number&lt;/p&gt;

&lt;p&gt;This problem gives you an array of distinct numbers ranging from 0 to n, with one number missing. Your task is to find that missing number.&lt;br&gt;
At first, I misread the instructions — I assumed the highest number would be the array's maximum value, not realizing that n is both the length of the array and the upper bound of the range [0, n]. That realisation changed everything for me.&lt;/p&gt;
&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;To solve this, we can use a well-known mathematical formula known as Gauss's formula. It calculates the sum of the first n natural numbers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;Sum&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works because you can pair numbers in the sequence so that each pair adds up to the same value, take the numbers between 1 and 100:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;1 + 100 &lt;span class="o"&gt;=&lt;/span&gt; 101  
2 + 99  &lt;span class="o"&gt;=&lt;/span&gt; 101  
3 + 98  &lt;span class="o"&gt;=&lt;/span&gt; 101  
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;n&lt;/code&gt; is multiplied by the number of possible pairings (&lt;code&gt;n&lt;/code&gt; + 1). Then as we're doing pairings we need to half this number.&lt;/p&gt;

&lt;p&gt;So, if the array had no missing number, its sum would match this formula. But since one number is missing, the actual sum of the array will be less than the expected sum.&lt;/p&gt;

&lt;p&gt;Thus, the missing number is simply:&lt;br&gt;
&lt;code&gt;Missing = Expected Sum - Actual Sum&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;MissingNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;expectedSum&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;actualSum&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;actualSum&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;expectedSum&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;actualSum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example Walkthrough
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
var numbers &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;0, 3, 1]&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c"&gt;# sum of all numbers&lt;/span&gt;
0 + 3 + 1 &lt;span class="o"&gt;=&lt;/span&gt; 4

&lt;span class="c"&gt;## all numbers added up using Gauss&lt;/span&gt;
&lt;span class="c"&gt;# 0 -&amp;gt; 3&lt;/span&gt;
n &lt;span class="o"&gt;=&lt;/span&gt; 3
3 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;3 + 1&lt;span class="o"&gt;)&lt;/span&gt; / 2 &lt;span class="o"&gt;=&lt;/span&gt; 6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we can then subtract 6 (Gauss sum) - 4 (actual sum) = 2&lt;/p&gt;

&lt;p&gt;So the missing number would be 2.&lt;/p&gt;

</description>
      <category>leetcode</category>
      <category>csharp</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>LeetCode 8: String to Integer (atoi) - Medium</title>
      <dc:creator>Grant Riordan</dc:creator>
      <pubDate>Sun, 21 Sep 2025 20:58:52 +0000</pubDate>
      <link>https://dev.to/grantdotdev/leetcode-8-string-to-integer-atoi-medium-33om</link>
      <guid>https://dev.to/grantdotdev/leetcode-8-string-to-integer-atoi-medium-33om</guid>
      <description>&lt;p&gt;Mastering LeetCode 8: String to Integer (atoi) &lt;/p&gt;

&lt;h2&gt;
  
  
  A Simple and Efficient Solution
&lt;/h2&gt;

&lt;p&gt;LeetCode's String to Integer (atoi) is a classic medium-difficulty coding problem that challenges you to convert a string representation of a number into an actual integer. While it sounds straightforward, the problem comes with a few twists: you need to handle leading whitespace, account for positive or negative signs, and ensure the result stays within the 32-bit signed integer range. &lt;/p&gt;

&lt;p&gt;In this post, I'll walk you through a highly efficient solution that achieves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;0ms runtime (beating 100% of submissions)&lt;/li&gt;
&lt;li&gt;41.85MB of memory (better than 50% of submissions) &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding the Problem
&lt;/h2&gt;

&lt;p&gt;The goal is to take a string, such as "   -42", "4193 with words", or "+123", and convert it into an integer, like -42, 4193, or 123. Here are the key requirements&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Ignore leading whitespace: Skip any spaces at the start of the string.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handle signs: Recognise + or - to determine if the number is positive or negative.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stop at invalid characters: If you encounter a non-digit (other than the initial sign), stop processing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Respect 32-bit integer bounds: If the result exceeds the 32-bit signed integer range (-2^31 to 2^31 - 1), return the appropriate boundary value (-2^31 or 2^31 - 1).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Default to 0: If the string is empty or contains no valid digits, return 0.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My Approach: Simple and Fast
&lt;/h2&gt;

&lt;p&gt;I designed a solution that prioritises efficiency by working directly with the string's characters and using indexing to avoid unnecessary operations. Here's the step-by-step approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Check for Empty Input: If the string is null or empty, return 0.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Skip Whitespace: Loop through the string to bypass leading spaces, avoiding methods like .Trim() to reduce overhead.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handle the Sign: Assume the number is positive by default (sign = 1). If a &lt;code&gt;+&lt;/code&gt; or &lt;code&gt;-&lt;/code&gt; is found, update the sign variable and move to the next character.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build the Number: Process each digit character, converting it to its numeric value and building the result incrementally. Stop if a non-digit is encountered.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check Bounds: As the number is built, ensure it stays within the 32-bit integer limits, clamping to &lt;code&gt;int.MaxValue&lt;/code&gt; or &lt;code&gt;int.MinValue&lt;/code&gt; if necessary.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apply the Sign: Multiply the result by the sign to get the correct positive or negative value.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach is clean, avoids heavy string operations, and handles all edge cases efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Solution&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;MyAtoi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Handle null or empty string&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Skip leading whitespace&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Return 0 if only whitespace&lt;/span&gt;

        &lt;span class="c1"&gt;// Check for sign&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;sign&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Default to positive&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'+'&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;sign&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Set sign based on + or -&lt;/span&gt;
            &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// Process digits&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsDigit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Convert character to digit (e.g., '5' -&amp;gt; 5)&lt;/span&gt;
            &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;digit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sc"&gt;'0'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// Check for overflow before adding digit&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MaxValue&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;digit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sign&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MaxValue&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MinValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="c1"&gt;// Build number: multiply by 10 and add new digit&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;digit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Apply sign to get final positive or negative result&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;Whitespace Handling: The &lt;code&gt;while (idx &amp;lt; end &amp;amp;&amp;amp; s[idx] == ' ')&lt;/code&gt; loop skips leading spaces. If the string contains only whitespace, we return 0.&lt;/p&gt;

&lt;p&gt;Sign Detection: We default to &lt;code&gt;sign = 1&lt;/code&gt; (positive). If a &lt;code&gt;+&lt;/code&gt; or &lt;code&gt;-&lt;/code&gt; is found, we set sign to 1 or -1 and advance the index (so the index will be at the first character).&lt;/p&gt;

&lt;p&gt;Number Building: For each digit, we convert the character to its numeric value (e.g., '5' - '0' = 5). We build the number by multiplying the current result by 10 and adding the new digit (e.g., for "123", we do 0*10 + 1 = 1, 1*10 + 2 = 12, 12*10 + 3 = 123).&lt;/p&gt;

&lt;p&gt;Overflow Check: Before adding a digit, we check if result * 10 + digit would exceed int.MaxValue. If so, we return int.MaxValue or int.MinValue based on the sign.&lt;/p&gt;

&lt;p&gt;Final Sign Application: Multiplying result by sign ensures the correct positive or negative output (e.g., 123 * -1 = -123).&lt;/p&gt;

&lt;p&gt;Hope this has helped, as always give me a follow, or drop me a line on &lt;a href="https://x.com/grantdotdev" rel="noopener noreferrer"&gt;twitter/x&lt;/a&gt;&lt;/p&gt;

</description>
      <category>leetcode</category>
      <category>csharp</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>LeetCode 206: Reverse Linked List - (Easy)</title>
      <dc:creator>Grant Riordan</dc:creator>
      <pubDate>Tue, 16 Sep 2025 13:37:28 +0000</pubDate>
      <link>https://dev.to/grantdotdev/leetcode-206-reverse-linked-list-easy-2l6h</link>
      <guid>https://dev.to/grantdotdev/leetcode-206-reverse-linked-list-easy-2l6h</guid>
      <description>&lt;p&gt;Given the head of a singly linked list, reverse the list, and return the reversed list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fte99qnkzslnq1axiixa0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fte99qnkzslnq1axiixa0.png" alt="example of reversal" width="542" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach &amp;amp; Solution
&lt;/h2&gt;

&lt;p&gt;We reverse a linked list by keeping track of two pointers: &lt;code&gt;prev&lt;/code&gt; (the previous node) and &lt;code&gt;curr&lt;/code&gt; (the current node). At each step, we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Save the next node so we don’t lose track of the list.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reverse the current node’s pointer to point to prev.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Move &lt;code&gt;prev&lt;/code&gt; forward to &lt;code&gt;curr&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Move &lt;code&gt;curr&lt;/code&gt; forward to the saved next node.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We continue until &lt;code&gt;curr&lt;/code&gt; is null, meaning we’ve processed all nodes. At that point, prev will be the new head of the reversed list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;ListNode ReverseList(ListNode head)&lt;br&gt;
{&lt;br&gt;
    ListNode prev = null;&lt;br&gt;
    ListNode curr = head;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;while (curr != null)
{
    ListNode nextNode = curr.next;
    curr.next = prev;
    prev = curr;
    curr = nextNode;
}

return prev; // new head
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>leetcode</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Extension Members: My New Favourite Feature in C# 14</title>
      <dc:creator>Grant Riordan</dc:creator>
      <pubDate>Fri, 12 Sep 2025 18:49:31 +0000</pubDate>
      <link>https://dev.to/grantdotdev/extension-members-my-new-favourite-feature-in-c-14-23f1</link>
      <guid>https://dev.to/grantdotdev/extension-members-my-new-favourite-feature-in-c-14-23f1</guid>
      <description>&lt;p&gt;The release candidate of .Net10 was released on the 9th September [2025], which comes with C# 14, and with that comes one of my favourite features, &lt;strong&gt;Extension Members&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Concept
&lt;/h2&gt;

&lt;p&gt;The new &lt;code&gt;extension&lt;/code&gt; keyword defines an extension scope; a block where you declare which type your extension methods apply to. All methods inside that scope automatically extend the specified type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before and After
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Current Implementation
&lt;/h3&gt;

&lt;p&gt;So up until C# 14 you would declare an extension method like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StringExtensions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ToTitleCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
         &lt;span class="n"&gt;CultureInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentCulture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToTitleCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can be cumbersome, and to some junior developers it may not be obvious that the method is an extension method. You would have to know that &lt;code&gt;static + this = extension&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The New Way
&lt;/h3&gt;

&lt;p&gt;Firstly you declare your extensions scope using the new &lt;code&gt;extension&lt;/code&gt; keyword:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StringExtensions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nf"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// extension methods go here&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This replaces the &lt;code&gt;(this string text)&lt;/code&gt; syntax, now all methods within this block apply to this type. The new syntax makes declaring all your extensions for this type much cleaner and tidier.&lt;/p&gt;

&lt;p&gt;Let's add some basic extensions to show you the concepts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StringExtensions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nf"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ToTitleCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;CultureInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentCulture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToTitleCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsPalindrome&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SequenceEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Reverse&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The examples above show just how easy it is to now declare multiple extension methods on the same type, because we can just use the scoped variable &lt;code&gt;text&lt;/code&gt; , rather than having to redefine it each time with &lt;code&gt;this&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shorter Method Declarations:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// before&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DateTimeExtensions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsWeekend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DayOfWeek&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;DayOfWeek&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Saturday&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="n"&gt;DayOfWeek&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sunday&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//after&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DateTimeExtensions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsWeekend&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DayOfWeek&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;DayOfWeek&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Saturday&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="n"&gt;DayOfWeek&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sunday&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsBusinessHours&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hour&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;17&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsWeekend&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Extensions Can Easily Be Grouped
&lt;/h2&gt;

&lt;p&gt;Before C# 14, you might see a single Helpers class like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Extensions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// for DateTime&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsWeekend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// for collections&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;HasDuplicates&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// for strings&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ToTitleCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// and so on...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem with this approach:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hard to maintain&lt;/strong&gt;– Over time, these Helpers classes can balloon to 500–1,000+ lines of mixed methods, making it difficult for developers to find what they need. – One file can contain extensions for multiple unrelated types.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Before C# 14&lt;/th&gt;
&lt;th&gt;After C# 14&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Helpers.cs (mixed types)&lt;/td&gt;
&lt;td&gt;Extensions.cs (type-scoped)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;- DateTime extensions&lt;/td&gt;
&lt;td&gt;- extension(DateTime date) { … }&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;- IEnumerable extensions&lt;/td&gt;
&lt;td&gt;- extension(IEnumerable items) { … }&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;- string extensions&lt;/td&gt;
&lt;td&gt;- extension(string text) { … }&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;- Random other methods&lt;/td&gt;
&lt;td&gt;… neatly grouped per type&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let's take a look how the code may look with the new groupings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Extensions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsWeekend&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsBankHoliday&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;FormatIsoDate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;HasDuplicates&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ToTitleCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means:&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Natural grouping by type *&lt;/em&gt;– all string extensions must sit inside the extension(string) scope, all DateTime ones inside extension(DateTime).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prevents mixing unrelated stuff&lt;/strong&gt;– you literally cannot write a &lt;code&gt;string&lt;/code&gt; extension in the &lt;code&gt;DateTime&lt;/code&gt; scope. The compiler enforces it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cleaner discoverability&lt;/strong&gt;– IDEs and developers now know exactly where to look. If you collapse your editor, you’ll see one &lt;code&gt;extension(string)&lt;/code&gt; block, one &lt;code&gt;extension(DateTime)&lt;/code&gt; block, instead of a 500+ line god like class of random extensions.&lt;/p&gt;

&lt;p&gt;Before, people dumped everything into Helpers.cs.&lt;/p&gt;

&lt;p&gt;Now, even if your team of developers decides to keep a single file called Extensions.cs, the syntax itself enforces organisation by type, so it’s much less likely to create a “misc soup” of extensions scattered around the file.&lt;/p&gt;

&lt;h2&gt;
  
  
  We Now Have Extension Properties !
&lt;/h2&gt;

&lt;p&gt;New in C#14 we no longer just have to have extension methods, we can now utilise &lt;em&gt;extension properties&lt;/em&gt;. Meaning without huge refactors, or updating multiple places you can easily add Extension properties, to compute results.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsNullOrEmpty&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You now have a &lt;code&gt;IsNullOrEmpty&lt;/code&gt; property which will be attached to every IEnumerable type within your code base.&lt;/p&gt;

&lt;p&gt;So you can do things like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Grant Green"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Roger Red"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Bianca Blue"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// Before (method style)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No names found!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// After C# 14 (property style)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No names found!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I think you'll agree using a property instead of a method makes the code read like it's part of the type itself. It's cleaner, easier to read, and removes the mental overhead of remembering parentheses for simple checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;C# 14’s extension members are a small language change with a big impact!&lt;/p&gt;

&lt;p&gt;They enforce better organisation by type, reducing the mess of “miscellaneous helpers” spread across static classes.&lt;/p&gt;

&lt;p&gt;Extension properties make your APIs feel more natural, allowing boolean checks and computed values to read like first-class members of the type.&lt;/p&gt;

&lt;p&gt;Overall, your code becomes cleaner, more discoverable, and easier for teams to maintain.&lt;/p&gt;

&lt;p&gt;While the compiler doesn’t stop you from scattering extensions across multiple classes, using a consistent type-scoped approach helps keep your codebase tidy and readable.&lt;/p&gt;

&lt;p&gt;In short: if you frequently write helper methods or utilities, C# 14 extension members are worth adopting — they make your codebase feel more structured and your extensions feel like they belong.&lt;/p&gt;

&lt;p&gt;Which types do you most often extend in your projects? I’d love to hear your thoughts in the comments or drop me a follow on &lt;a href="https://x.com/grantdotdev" rel="noopener noreferrer"&gt;twitter/x&lt;/a&gt; to hear when I post more articles like this on DevTo and FreeCodeCamp.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>tutorial</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>C# LeetCode 1792: Maximum Average Pass Ratio - (Medium)</title>
      <dc:creator>Grant Riordan</dc:creator>
      <pubDate>Thu, 04 Sep 2025 18:14:59 +0000</pubDate>
      <link>https://dev.to/grantdotdev/c-leetcode-1792-maximum-average-pass-ratio-medium-199d</link>
      <guid>https://dev.to/grantdotdev/c-leetcode-1792-maximum-average-pass-ratio-medium-199d</guid>
      <description>&lt;p&gt;&lt;strong&gt;LeetCode 1792: Maximum Average Pass Ratio - (Medium Difficulty)&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Intuition
&lt;/h2&gt;

&lt;p&gt;Needed a way to determine which class would yield the best return on adding the extra students.&lt;/p&gt;

&lt;p&gt;Best way to track this in my mind would be tracking a Priority, so a &lt;code&gt;PriorityQueue&lt;/code&gt; would be ideal for this scenario. A &lt;code&gt;PriorityQueue&lt;/code&gt; lets us always select the class with the current highest gain efficiently in O(log n) time per operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Populate the queue with initial values and the gain in pass ratio from adding one student to each class.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For each student, take the class with the highest gain from adding one more passing student.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Loop over the priority queue adding up all the ratios and divide by number of classes to get the average ratio.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;Let's say you have two classes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;th&gt;Pass&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;th&gt;Initial Gain from 1 Student&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0.1667&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;0.0667&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;First assignment:&lt;/p&gt;

&lt;p&gt;Class A has higher gain → gets 1 student → now A = [2,3]&lt;/p&gt;

&lt;p&gt;Next round:&lt;/p&gt;

&lt;p&gt;New gain for A:&lt;br&gt;
(3/4) - (2/3) = 0.75 - 0.6667 = 0.0833&lt;/p&gt;

&lt;p&gt;Class B still has gain:&lt;br&gt;
(4/6) - (3/5) = 0.6667 - 0.6 = 0.0667&lt;/p&gt;

&lt;p&gt;→ A still has higher gain (0.0833 vs 0.0667), so gets another student.&lt;/p&gt;

&lt;p&gt;Now A = [3,4]&lt;/p&gt;

&lt;p&gt;Third round:&lt;/p&gt;

&lt;p&gt;Gain for A now:&lt;br&gt;
(4/5) - (3/4) = 0.8 - 0.75 = 0.05&lt;/p&gt;

&lt;p&gt;Class B gain is still 0.0667&lt;/p&gt;

&lt;p&gt;✅ Now B has higher gain — it gets the next student.&lt;/p&gt;

&lt;p&gt;When solving this problem, you might wonder:&lt;/p&gt;

&lt;p&gt;“Aren’t we already adding one extra student when we calculate the initial gain? Then adding more? Isn’t that overcounting?”&lt;/p&gt;

&lt;p&gt;The answer is no — and here’s why.&lt;/p&gt;

&lt;p&gt;The iniital gain is just a prediction. When we calculate the gain for a class:&lt;/p&gt;

&lt;p&gt;Gain(pass, total) = Ratio(pass + 1, total + 1) - Ratio(pass, total)&lt;/p&gt;

&lt;p&gt;We're not actually adding a student.&lt;br&gt;
We're just asking a "what if" question:&lt;/p&gt;

&lt;p&gt;“If I gave one extra passing student to this class, how much would its pass ratio improve?”&lt;/p&gt;

&lt;p&gt;This helps us prioritize which class would benefit the most if it were chosen next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Solution&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;MaxAverageRatio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[][]&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;extraStudents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pq&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;PriorityQueue&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Comparer&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompareTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Populate the queue with initial values&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pass&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="n"&gt;pq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Enqueue&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;Gain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Distribute extra students&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;extraStudents&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dequeue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
            &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
            &lt;span class="n"&gt;pq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Enqueue&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;Gain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// After distribution, calculate final average&lt;/span&gt;
        &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dequeue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;CalculateRatio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;CalculateRatio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;Gain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;CalculateRatio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;CalculateRatio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>csharp</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>leetcode</category>
    </item>
    <item>
      <title>LeetCode 168: Excel Sheet Column Title (Easy)</title>
      <dc:creator>Grant Riordan</dc:creator>
      <pubDate>Sat, 30 Aug 2025 17:00:11 +0000</pubDate>
      <link>https://dev.to/grantdotdev/leetcode-168-excel-sheet-column-title-easy-3mbc</link>
      <guid>https://dev.to/grantdotdev/leetcode-168-excel-sheet-column-title-easy-3mbc</guid>
      <description>&lt;h1&gt;
  
  
  Approach
&lt;/h1&gt;

&lt;p&gt;This problem is essentially a &lt;strong&gt;base-26 conversion&lt;/strong&gt;, where the "digits" are the 26 letters of the alphabet (&lt;code&gt;A–Z&lt;/code&gt;).  &lt;/p&gt;

&lt;p&gt;Like any base conversion, we process the &lt;strong&gt;least significant digit first&lt;/strong&gt; — meaning we figure out the &lt;em&gt;last letter&lt;/em&gt; of the result before the first.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use the modulo operator (&lt;code&gt;% 26&lt;/code&gt;) to find the remainder, which tells us the current character.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Insert each character at the &lt;strong&gt;front&lt;/strong&gt; of the string (or reverse later), since we’re building the result from right → left. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After each step, divide &lt;code&gt;columnNumber&lt;/code&gt; by 26 to move on to the next "digit."  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Important: decrement &lt;code&gt;columnNumber&lt;/code&gt; at the start of each loop (&lt;code&gt;columnNumber--&lt;/code&gt;) because Excel columns are &lt;strong&gt;1-based&lt;/strong&gt;, while our alphabet mapping is &lt;strong&gt;0-based&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Example Flow (columnNumber = 28)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Iteration 1&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input: 28
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;columnNumber--&lt;/code&gt; → 27
&lt;/li&gt;
&lt;li&gt;Remainder: &lt;code&gt;27 % 26 = 1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Character: &lt;code&gt;'A' + 1 = 'B'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Result: &lt;code&gt;"B"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Update columnNumber: &lt;code&gt;27 / 26 = 1&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Iteration 2&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;columnNumber--&lt;/code&gt; → 0
&lt;/li&gt;
&lt;li&gt;Remainder: &lt;code&gt;0 % 26 = 0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Character: &lt;code&gt;'A' + 0 = 'A'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Result: &lt;code&gt;"AB"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Update columnNumber: &lt;code&gt;0 / 26 = 0&lt;/code&gt; → stop
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ Final result = &lt;code&gt;"AB"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We can verify manually:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;27 = 1 full cycle (&lt;code&gt;A → Z&lt;/code&gt;) + 2 more columns (&lt;code&gt;AA, AB&lt;/code&gt;).
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
csharp
public class Solution {
    public string ConvertToTitle(int columnNumber) {
        var result = new StringBuilder();

        while (columnNumber &amp;gt; 0) {
            columnNumber--; // shift into 0-based
            int remainder = columnNumber % 26;
            result.Insert(0, (char)('A' + remainder));
            columnNumber /= 26;
        }

        return result.ToString();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>csharp</category>
      <category>leetcode</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Stop Throwing Exceptions: The ServiceResult&lt;T&gt; Pattern for Clean API</title>
      <dc:creator>Grant Riordan</dc:creator>
      <pubDate>Fri, 29 Aug 2025 13:56:37 +0000</pubDate>
      <link>https://dev.to/grantdotdev/stop-throwing-exceptions-the-serviceresult-pattern-for-clean-api-5fj1</link>
      <guid>https://dev.to/grantdotdev/stop-throwing-exceptions-the-serviceresult-pattern-for-clean-api-5fj1</guid>
      <description>&lt;p&gt;Error handling in APIs can get pretty messy, right?&lt;/p&gt;

&lt;p&gt;Whether it's throwing exceptions for "expected" situations like “User not found,” or dealing with ambiguous null values, traditional error handling approaches often leave us with more questions than answers. But don't worry—there's a better way!&lt;/p&gt;

&lt;p&gt;Imagine you're a waiter taking orders: if a customer orders a dish that’s not available, you don’t throw a tantrum  or drop everything in the kitchen (throw Exception). Instead, you let them know it’s not available and suggest something else. Same idea applies to your API!&lt;/p&gt;

&lt;p&gt;In this post, I’ll walk you through how the Discriminated Union pattern (using a ServiceResult type) can simplify your error handling, making your code more predictable and easy to maintain.&lt;/p&gt;

&lt;p&gt;In simple terms, a &lt;strong&gt;Discriminated Union&lt;/strong&gt; is a way to define a type that can be one of several distinct types, each with its own properties.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Why Traditional Error Handling Doesn’t Cut It&lt;/li&gt;
&lt;li&gt;The ServiceResult Pattern&lt;/li&gt;
&lt;li&gt;Usage Example&lt;/li&gt;
&lt;li&gt;Benefits of This Approach&lt;/li&gt;
&lt;li&gt;Real-World Error Handling&lt;/li&gt;
&lt;li&gt;Separation of Concerns&lt;/li&gt;
&lt;li&gt;Flow of Code Example&lt;/li&gt;
&lt;li&gt;Caveats and Considerations&lt;/li&gt;
&lt;li&gt;Final Thoughts&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why Traditional Error Handling Doesn’t Cut It
&lt;/h2&gt;

&lt;p&gt;When building APIs or services, one of the first design decisions you’ll face is how to represent the outcome of an operation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Exceptions aren’t always exceptional (e.g. “User not found” is normal, not an error).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Nulls are ambiguous (is it missing, or did something go wrong?).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wrapping results in a &lt;code&gt;ServiceResult&amp;lt;T&amp;gt;&lt;/code&gt; gives clarity: every call is either a success or a failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ServiceResult Pattern
&lt;/h2&gt;

&lt;p&gt;At its core, every service call has two possible outcomes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It succeeds, giving you the entity / object you wanted.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It fails, giving you structured error details.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In functional programming, this is often called a &lt;strong&gt;Discriminated Union&lt;/strong&gt; (a type with a fixed set of possibilities). You may be more familiar with union types if using for example Typescript.&lt;br&gt;
In C# these don't exist, however, we can express this cleanly with &lt;code&gt;records&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Is is my interpretation is below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TEntity&lt;/span&gt; &lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;Failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;Errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ErrorType&lt;/span&gt; &lt;span class="n"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;FromSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TEntity&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;FromFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ErrorType&lt;/span&gt; &lt;span class="n"&gt;errorType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorType&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below is the implementation of the ErrorType enum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;ErrorType&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Validation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Conflict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Unexpected&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Here’s what’s happening:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Success&lt;/strong&gt; → wraps the entity so you know the operation worked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure&lt;/strong&gt; → captures one or more error messages and an error type (NotFound, Conflict, etc.).&lt;/p&gt;

&lt;p&gt;Instead of manually creating new instances of &lt;code&gt;Success&lt;/code&gt; or &lt;code&gt;Failure&lt;/code&gt;, the factory methods &lt;code&gt;FromSuccess&lt;/code&gt; and &lt;code&gt;FromFailure&lt;/code&gt; let you do it with one simple line of code. It's like a shortcut to avoid any mistakes or extra boilerplate.&lt;/p&gt;

&lt;p&gt;This gives you a predictable, strongly-typed contract. Whenever you call a service, you must deal with either Success or Failure. &lt;/p&gt;

&lt;p&gt;Here is an example of how ServiceResult and ErrorTypes could be used:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Case&lt;/th&gt;
&lt;th&gt;What you get&lt;/th&gt;
&lt;th&gt;Why it’s useful&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Entity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Normal outcome, strongly typed result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Errors[]&lt;/code&gt;, &lt;code&gt;ErrorType&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Clear reason for failure, caller knows what broke&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validation Failure&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Errors[]&lt;/code&gt;, &lt;code&gt;Validation&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Communicates bad input without exceptions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Not Found&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Errors[]&lt;/code&gt;, &lt;code&gt;NotFound&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Expresses absence cleanly (instead of null/404)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conflict&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Errors[]&lt;/code&gt;, &lt;code&gt;Conflict&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Useful for concurrency/duplicate key scenarios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database/Server&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Errors[]&lt;/code&gt;, &lt;code&gt;Database&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Lets you bubble up infra issues in a controlled way&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unexpected Exception&lt;/td&gt;
&lt;td&gt;(thrown, not returned)&lt;/td&gt;
&lt;td&gt;Reserved for truly exceptional conditions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If the operation fails, you get one or more error messages and an ErrorType enum.&lt;/p&gt;

&lt;p&gt;This gives the caller enough context to decide what to do next — maybe return a 404, maybe log an error, maybe retry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage Example
&lt;/h2&gt;

&lt;p&gt;Now that we’ve got our ServiceResult type, let’s see what it looks like in practice.&lt;/p&gt;

&lt;p&gt;Imagine you’re building a movie service, and you need to update a movie. Instead of throwing an exception when the movie isn’t found, you return a Failure result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Service layer - MovieService.UpdateAsync()&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;UpdateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UpdateMovieRequest&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;movie&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetByIdAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;movie&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Movie not found"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotFound&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things to notice here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No exceptions&lt;/strong&gt; for expected outcomes (Movie not found is normal, not exceptional).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consistent shape:&lt;/strong&gt; every exit path returns a ServiceResult.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flexible:&lt;/strong&gt; you can add richer error context over time (Validation, Conflict, etc.).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then in your API handler (controller/endpoint), you can use pattern matching to decide what to return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;movieService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UpdateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="n"&gt;Success&lt;/span&gt; &lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="n"&gt;Failure&lt;/span&gt; &lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;MapErrorTypeToStatusCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errors&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InvalidOperationException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unexpected result type"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the controller stays thin — no exception handling logic, no try/catch bloat — just clear translation of service outcomes into HTTP responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of This Approach
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Predictable&lt;/strong&gt;: Callers must handle both success and failure, making bugs less likely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expressive&lt;/strong&gt;: You can return multiple errors, not just a single exception message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performant&lt;/strong&gt;: You avoid throwing/catching exceptions for normal control flow.&lt;/p&gt;

&lt;p&gt;Modern C#: Uses records and pattern matching — concise, readable, and type-safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Error Handling
&lt;/h2&gt;

&lt;p&gt;So far we’ve only looked at simple success/failure outcomes, but real-world services often hit database or infrastructure errors. Traditionally, you’d wrap service logic in try/catch and bubble exceptions up to the controller — which makes your API code messy and inconsistent.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;ServiceResult&amp;lt;TEntity&amp;gt;&lt;/code&gt;, we can catch exceptions inside the service and still return a clean, predictable result.&lt;/p&gt;

&lt;p&gt;Here’s an example for deleting a movie:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;DeleteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;movieResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GetByIdAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;movieResult&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="n"&gt;Failure&lt;/span&gt; &lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DeleteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DbUpdateException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Database error while deleting movie {MovieId}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;$"Failed to delete movie: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what’s happening here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected errors&lt;/strong&gt; (movie not found) are modeled as failures with ErrorType.NotFound.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unexpected errors&lt;/strong&gt; (like DbUpdateException) are caught and translated into &lt;code&gt;ErrorType.Database&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The calling code doesn’t need to care whether it was a validation error, missing data, or a DB issue — it just sees Failure and can respond appropriately.&lt;/p&gt;

&lt;p&gt;Here’s what the API handler looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;HandleAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DeleteMovieRequest&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;movieService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DeleteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="n"&gt;Failure&lt;/span&gt; &lt;span class="n"&gt;failedResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;failedResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;AddError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ErrorsAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;MapErrorTypeToStatusCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;failedResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="n"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Deleted movie with ID: {MovieId}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OkAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DeleteMovieResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No try/catch in the controller, no exception mapping logic scattered around.&lt;br&gt;
All failure handling is centralized in the service, while the API handler simply translates ErrorType → HTTP status code.&lt;/p&gt;
&lt;h2&gt;
  
  
  Separation of Concerns:
&lt;/h2&gt;

&lt;p&gt;Encourages separation of concerns as services don’t need to know about HTTP or status codes.&lt;/p&gt;

&lt;p&gt;Endpoints/controllers can translate ServiceResult.Failure → 400/404/500 as appropriate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;ServiceResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="n"&gt;Failure&lt;/span&gt; &lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorType&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Validation&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotFound&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Conflict&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;409&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ErrorsAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s a much cleaner alternative than handling multiple custom exception types in your API handler.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flow of Code Example:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;                   +----------------------+
                   |   Service Call       |
                   +----------------------+
                            |
                            v
             +------------------------------+
             |  Was the operation a success?|
             +------------------------------+
              /                        &lt;span class="err"&gt;\&lt;/span&gt;
             /                          &lt;span class="err"&gt;\&lt;/span&gt;
            v                            v
+-------------------+         +--------------------------+
|   Success         |         |   Failure                |
|-------------------|         |--------------------------|
|   Return Entity   |         |   Error Type (e.g.       |
|   (TEntity)       |         |   Validation, NotFound,  |
+-------------------+         |   Conflict, Database,    |
                              |   Unexpected)            |
                              +--------------------------+
                                       |
                                       v
                           +--------------------------+
                           | Return Error Details     |
                           | (Errors[], ErrorType)    |
                           +--------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Caveats and Considerations
&lt;/h2&gt;

&lt;p&gt;While the ServiceResult pattern is straightforward and provides clarity, it's important to be mindful of the context in which it's applied. In smaller applications with very simple error handling needs, this pattern might feel like overkill, but as your system grows and complexity increases, the benefits far outweigh the initial overhead. &lt;/p&gt;

&lt;p&gt;One potential challenge is ensuring that your &lt;code&gt;ErrorType&lt;/code&gt; enum is comprehensive enough to cover all potential failure scenarios. If you miss a common error type, the pattern might lose some of its expressiveness. However, with proper design and planning, this pattern keeps error handling clean, maintainable, and easy to extend as your app evolves. Ultimately, it encourages good separation of concerns and helps avoid clutter in your API handlers.&lt;/p&gt;

&lt;p&gt;The key takeaway is that it offers a solid, scalable foundation for error handling, and once in place, it keeps your codebase more predictable and robust in the long run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;For expected outcomes (like a user not found or bad input), use &lt;code&gt;ServiceResult&amp;lt;T&amp;gt;&lt;/code&gt; to return a structured Failure result. This is part of your normal control flow.&lt;/p&gt;

&lt;p&gt;For truly exceptional, unexpected issues (like a database connection failure), let exceptions be thrown. This prevents messy try/catch blocks in your service and API layers.&lt;/p&gt;

&lt;p&gt;Centralise logic; by catching infrastructure exceptions (like &lt;code&gt;DbUpdateException&lt;/code&gt;) inside your service and converting them to a &lt;code&gt;ServiceResult.Failure&lt;/code&gt;, you keep your API handlers clean and focused on translating outcomes into HTTP responses."&lt;/p&gt;

&lt;p&gt;What do you think of this pattern? Do you use a similar approach, or do you prefer a different method for handling API errors? Share your thoughts in the comments!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>csharp</category>
      <category>api</category>
    </item>
    <item>
      <title>When Benchmarks Lie: A Case of “0ns” String Comparisons</title>
      <dc:creator>Grant Riordan</dc:creator>
      <pubDate>Tue, 12 Aug 2025 10:55:01 +0000</pubDate>
      <link>https://dev.to/grantdotdev/when-benchmarks-lie-a-case-of-0ns-string-comparisons-3m7i</link>
      <guid>https://dev.to/grantdotdev/when-benchmarks-lie-a-case-of-0ns-string-comparisons-3m7i</guid>
      <description>&lt;p&gt;Recently, while reviewing code, I spotted a colleague using ToLower() for a case-insensitive string comparison instead of string.Equals().&lt;br&gt;
When I asked why, the answer was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I thought it would be faster.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Rather than argue, we fired up &lt;code&gt;BenchmarkDotNet&lt;/code&gt;. The results? Surprising — and completely misleading.&lt;/p&gt;

&lt;p&gt;This turned into a great reminder that benchmarks can lie if you don’t understand what’s really being tested.&lt;br&gt;
The Test Code&lt;/p&gt;

&lt;p&gt;Let’s say we have a ValidationService like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ValidationService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Expected&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Grant RiOrDan"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsValid_StringEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsValid_ToLower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Expected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsValid_ToUpper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToUpper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Expected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToUpper&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsValid_Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Our goal&lt;/strong&gt;: benchmark each method for execution time and memory usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Round 1: The Wrong Benchmark
&lt;/h2&gt;

&lt;p&gt;Here’s the initial benchmark setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MemoryDiagnoser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StringComparisonBenchmarks&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;LowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ValidationService&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;IsValid_ToLower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Grant Riordan"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;UpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ValidationService&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;IsValid_ToUpper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Grant Riordan"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ValidationService&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;IsValid_Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Grant Riordan"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;StringEquals&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ValidationService&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;IsValid_StringEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Grant Riordan"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Results:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Mean&lt;/th&gt;
&lt;th&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LowerCase&lt;/td&gt;
&lt;td&gt;30.49 ns&lt;/td&gt;
&lt;td&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UpperCase&lt;/td&gt;
&lt;td&gt;29.83 ns&lt;/td&gt;
&lt;td&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Equals&lt;/td&gt;
&lt;td&gt;0.00 ns&lt;/td&gt;
&lt;td&gt;0 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;StringEquals&lt;/td&gt;
&lt;td&gt;0.00 ns&lt;/td&gt;
&lt;td&gt;0 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Wait… &lt;code&gt;Equals()&lt;/code&gt; and &lt;code&gt;string.Equals()&lt;/code&gt; take 0 nanoseconds and allocate no memory?&lt;/p&gt;

&lt;p&gt;Did we just discover the fastest string comparison in the universe?&lt;/p&gt;

&lt;h2&gt;
  
  
  What Really Happened
&lt;/h2&gt;

&lt;p&gt;Nope. This is &lt;strong&gt;constant folding&lt;/strong&gt; in action.&lt;/p&gt;

&lt;p&gt;Both Expected and the benchmark input "Grant Riordan" are compile-time constants. The C# compiler and JIT realise the comparison will always return the same value (true) and replace the whole method call with a constant of &lt;code&gt;true&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In other words, our “benchmark” was actually measuring:&lt;/p&gt;

&lt;p&gt;return true;&lt;/p&gt;

&lt;p&gt;That’s why it took &lt;code&gt;0ns&lt;/code&gt; — the method was optimised away entirely.&lt;/p&gt;

&lt;p&gt;Meanwhile, &lt;code&gt;ToLower()&lt;/code&gt; and &lt;code&gt;ToUpper()&lt;/code&gt; couldn’t be optimised in the same way because they create new strings at runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Can We Fix This? Round 2: The Correct Benchmark
&lt;/h2&gt;

&lt;p&gt;To get a real measurement, we need to make sure our inputs aren’t compile-time constants. That forces the JIT to execute the comparison logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MemoryDiagnoser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StringComparisonBenchmarks&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ValidationService&lt;/span&gt; &lt;span class="n"&gt;_service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;_userInput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Grant Riordan"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;LowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsValid_ToLower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_userInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;UpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsValid_ToUpper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_userInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsValid_Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_userInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;StringEquals&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsValid_StringEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_userInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Results:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Mean&lt;/th&gt;
&lt;th&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LowerCase&lt;/td&gt;
&lt;td&gt;31.05 ns&lt;/td&gt;
&lt;td&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UpperCase&lt;/td&gt;
&lt;td&gt;30.75 ns&lt;/td&gt;
&lt;td&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Equals&lt;/td&gt;
&lt;td&gt;0.55 ns&lt;/td&gt;
&lt;td&gt;0 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;StringEquals&lt;/td&gt;
&lt;td&gt;0.72 ns&lt;/td&gt;
&lt;td&gt;0 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;Equals()&lt;/code&gt; and &lt;code&gt;string.Equals()&lt;/code&gt; are far faster and allocate no memory.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ToLower()&lt;/code&gt; / &lt;code&gt;ToUpper()&lt;/code&gt; are much slower here because they create new strings every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lesson
&lt;/h2&gt;

&lt;p&gt;This post isn’t about proving which method is fastest (though OrdinalIgnoreCase is generally the best choice for case-insensitive equality checks).&lt;/p&gt;

&lt;p&gt;It’s about understanding what you’re actually benchmarking.&lt;/p&gt;

&lt;p&gt;If the results look too good to be true, they probably are.&lt;br&gt;
In this case, the culprit was constant folding — and without realising it, we were benchmarking a &lt;code&gt;return true;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Always sanity-check your benchmark setup. Even small changes in how you pass values can make the difference between measuring actual work… and measuring nothing at all.&lt;/p&gt;
&lt;h3&gt;
  
  
  Bonus Tip: OrdinalIgnoreCase vs InvariantCultureIgnoreCase
&lt;/h3&gt;

&lt;p&gt;In our examples, we used:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;StringComparison.OrdinalIgnoreCase&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is the fastest way to do a case-insensitive equality check in most scenarios because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It does a simple binary (ordinal) comparison.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It only applies basic Unicode case folding (A–Z).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It avoids any cultural rules.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By contrast, &lt;code&gt;InvariantCultureIgnoreCase&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Performs a full culture-invariant comparison using Unicode collation rules. Culture-invariant means the string operations follow a fixed, culture-independent set of rules that never change regardless of the system's current locale or the user’s regional settings.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handles special casing rules (Turkish I, Greek sigma forms, etc.).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Is much slower because it’s doing a lot more work under the hood.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| Culture       | &lt;span class="sb"&gt;`"e"`&lt;/span&gt; vs &lt;span class="sb"&gt;`"é"`&lt;/span&gt; | Why                                                    |
| ------------- | -------------- | ------------------------------------------------------ |
| &lt;span class="gs"&gt;**Invariant**&lt;/span&gt; | Equal          | Uses English-like rules, ignores accents in comparison |
| &lt;span class="gs"&gt;**en-US**&lt;/span&gt;     | Equal          | English treats accented e as same as plain e           |
| &lt;span class="gs"&gt;**fr-FR**&lt;/span&gt;     | Not equal      | French considers accented e distinct                   |
| &lt;span class="gs"&gt;**Ordinal**&lt;/span&gt;   | Not equal      | Binary match — different Unicode code points           |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Performance takeaway:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;OrdinalIgnoreCase&lt;/code&gt; when comparing identifiers, filenames, or any data where cultural rules should not affect the comparison — i.e., culture-independent comparisons focusing on binary equality ignoring case.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;InvariantCultureIgnoreCase&lt;/code&gt; when you want a culture-agnostic but linguistically reasonable comparison, for example, normalizing or searching user-entered text in a way consistent across cultures, without specific cultural preferences.&lt;/p&gt;

&lt;p&gt;Use a specific culture (&lt;code&gt;CurrentCulture&lt;/code&gt; or a particular &lt;code&gt;CultureInfo&lt;/code&gt;) when the input must be interpreted or matched according to cultural and linguistic rules, such as formatting, sorting, or validating user input that is culture-sensitive.&lt;/p&gt;

&lt;p&gt;As always drop me a follow on here or &lt;a href="https://x.com/grantdotdev" rel="noopener noreferrer"&gt;twitter/x&lt;/a&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>programming</category>
      <category>testing</category>
      <category>learning</category>
    </item>
    <item>
      <title>C# LeetCode 136: Single Number - (Easy)</title>
      <dc:creator>Grant Riordan</dc:creator>
      <pubDate>Wed, 06 Aug 2025 17:51:30 +0000</pubDate>
      <link>https://dev.to/grantdotdev/c-leetcode-136-single-number-easy-310</link>
      <guid>https://dev.to/grantdotdev/c-leetcode-136-single-number-easy-310</guid>
      <description>&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Given a non-empty array of integers nums, every element appears twice except for one. Find that single one.&lt;/p&gt;

&lt;p&gt;You must implement a solution with a linear runtime complexity and use only constant extra space.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach &amp;amp; Initial Thinking
&lt;/h2&gt;

&lt;p&gt;Seems pretty simple, we need to count each number in the array and then return the one with the least count. &lt;/p&gt;

&lt;p&gt;My first go-to is again LINQ - you can learn more about LINQ in my FreeCodeCamp tutorial &lt;a href="https://www.freecodecamp.org/news/how-to-use-linq/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can use a combination of the &lt;code&gt;GroupBy&lt;/code&gt; and &lt;code&gt;OrderBy() Count()&lt;/code&gt; to count all the individual numbers and order them by count. &lt;/p&gt;

&lt;p&gt;All we have to do then is return the First() (lowest scoring) key (key is what we've grouped by). &lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;SingleNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GroupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;First&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Drop me a follow on &lt;a href="https://x.com/grantdotdev" rel="noopener noreferrer"&gt;twitter/x&lt;/a&gt; to hear about more articles in this series and other tech / developer tips.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>programming</category>
      <category>csharp</category>
      <category>leetcode</category>
    </item>
  </channel>
</rss>
