Skip to main content
SEMastery
.NET Coreintermediate

Extension Members in C# 14: A Friendly, Complete Guide for Beginners

Learn C# 14 extension members in simple words. Add properties, static members, and operators to types you do not own, with clear examples and diagrams.

11 min readUpdated November 24, 2025

Imagine you buy a nice steel lunchbox (a "tiffin" box). It works well, but it did not come with a small handle. You cannot open the factory and redesign the box. So you clip on your own handle from outside. The box is unchanged inside, but now it is easier to carry.

That clip-on handle is the perfect picture of an extension. You take something you did not build, and you add a useful helper on the outside, without touching the original.

C# has let us do this for instance methods for a long time. C# 14 (which ships with .NET 10) makes the idea much bigger. Now you can clip on properties, static members, and even operators, not just methods. The C# team calls this extension members. Some people happily call it "extension everything."

Let me walk you through it slowly, like a teacher sitting next to you.

A quick reminder: what extension methods already did

Before C# 14, you could add a method to a type you do not own. You write a static class and a static method, and you put this in front of the first parameter.

public static class StringHelpers
{
    // The "this string text" part is the magic.
    public static bool IsBlank(this string text)
    {
        return string.IsNullOrWhiteSpace(text);
    }
}
 
// Now you can call it like the method belongs to string:
string name = "   ";
bool empty = name.IsBlank(); // true

Notice the trick. IsBlank is really a normal static method. But because of this string text, the compiler lets you call it with dot syntax, as if string always had it. The lunchbox got a handle.

This was great. But there was a wall. You could only add methods. You could not add a property like name.IsBlank (no parentheses). You could not add a static helper like string.Empty. You were stuck with method-only helpers. C# 14 knocks down that wall.

The new idea: an extension block

C# 14 keeps the same outer shape (a static class), but inside it you can now write an extension block. You write the word extension, and in the parentheses you put the receiver — the thing you are extending.

public static class StringExtensions
{
    // "string text" is the receiver for everything inside this block.
    extension(string text)
    {
        // An extension PROPERTY (no parentheses when you use it).
        public bool IsBlank => string.IsNullOrWhiteSpace(text);
 
        // An extension METHOD, same as before but cleaner.
        public string Repeat(int times) => string.Concat(Enumerable.Repeat(text, times));
    }
}

Look at what changed. The receiver string text moved up into the extension(...) part. So every member inside the block can use text for free. You do not repeat this string text on each one. It reads almost like you opened up string and added members directly.

Now you can write code that was impossible before:

string code = "  HELLO  ";
 
if (code.IsBlank)        // property, no ()
{
    Console.WriteLine("nothing here");
}
 
string line = "ab".Repeat(3); // "ababab"

code.IsBlank with no parentheses is the new superpower. It feels like a real property on string.

Old extension methods vs new extension blocks at a glance

The four things you can add now

Extension blocks support several member kinds. Here is the simple list, with what each one is for.

Member kindOld C#?What it doesExample use
Instance methodYesActs on one valuetext.Repeat(3)
Instance propertyNo (new)A value with no ()text.IsBlank
Static memberNo (new)Belongs to the type itselfstring.Greeting
OperatorNo (new)Defines +, *, etc.vecA + vecB

Let me show each new kind with a tiny, friendly example.

1. Instance properties

An instance property reads like data, not an action. No parentheses.

public static class NumberExtensions
{
    extension(int number)
    {
        public bool IsEven => number % 2 == 0;
        public int Doubled => number * 2;
    }
}
 
int score = 10;
Console.WriteLine(score.IsEven);  // True
Console.WriteLine(score.Doubled); // 20

This is much nicer than calling score.IsEven() like a method. Even-ness is a fact about the number, so a property fits better.

2. Static extension members

This one surprises people. You can add a member to the type itself, not to an instance. To do that, you write an extension block where the receiver names the type but has no parameter name.

public static class StringExtensions
{
    extension(string) // note: just the type, no variable name
    {
        public static string Greeting => "Namaste!";
    }
}
 
// Now this works, as if string had a built-in Greeting:
Console.WriteLine(string.Greeting); // Namaste!

Before C# 14 this was simply not possible. You could never write string.Something for a helper you made. Now you can give a type new constants or computed values that live right where people expect them.

3. Operators

You can teach a type how to respond to symbols like +. This is handy for math-like types.

public static class PointExtensions
{
    extension(Point)
    {
        public static Point operator +(Point a, Point b)
            => new Point(a.X + b.X, a.Y + b.Y);
    }
}
 
var sum = new Point(1, 2) + new Point(3, 4); // Point(4, 6)

Now Point values add together with +, even if you did not write the Point type yourself.

How the compiler turns an extension block into normal static code

Why this matters (the real "before and after")

The big win is readability. Properties, static members, and operators let your helpers look natural. People reading your code do not need to know it came from an extension at all. It just works.

From idea to usable member

Pick a type
Open extension block
Add member
Use with dot syntax

Steps

1

Pick a type

string, int, your own class

2

Open extension block

extension(receiver)

3

Add member

property, static, operator

4

Use with dot syntax

feels built-in

The path a new extension member travels

Here is a side-by-side that shows the feeling difference. Same goal, two eras.

// C# 13 style: everything must be a method
bool blank = name.IsBlank();          // ()
string g = StringHelpers.Greeting();  // type helper hidden in a class
 
// C# 14 style: natural members
bool blank2 = name.IsBlank;           // property, no ()
string g2 = string.Greeting;          // lives on the type itself

The second version reads like English. That is the quiet revolution.

How it works under the hood

You might worry this is slow magic. It is not. Extension members are still compiled into plain static methods, exactly like the old extension methods. The compiler just rewrites your nice syntax into ordinary calls. There is no extra cost at run time. The type you extended is never changed in memory; you only get a friendlier way to call helpers.

This also means full backward compatibility. Your old this-style extension methods keep working. You can even mix both styles in the same static class. There is no forced migration. Add extension blocks only where they make your code clearer.

WorryReality
Is it slow?No. Compiles to normal static calls.
Does it change the original type?No. The type stays untouched.
Must I rewrite old code?No. Old extension methods still work.
Can I mix old and new?Yes, even in one file.

A fuller example you can relate to

Say you are building a small app for a shop. You work with decimal for prices. You want clean helpers: format as rupees, check if a price is free, and a shared tax rate on the type itself.

public static class PriceExtensions
{
    extension(decimal price)
    {
        // instance property
        public bool IsFree => price == 0m;
 
        // instance method
        public string AsRupees() => $"Rs. {price:0.00}";
 
        // instance method that uses a static member below
        public decimal WithTax() => price + (price * decimal.TaxRate);
    }
 
    extension(decimal)
    {
        // static property on the decimal type itself
        public static decimal TaxRate => 0.18m;
    }
}
 
decimal item = 250m;
Console.WriteLine(item.AsRupees());   // Rs. 250.00
Console.WriteLine(item.WithTax());    // 295.0
Console.WriteLine(item.IsFree);       // False
Console.WriteLine(decimal.TaxRate);   // 0.18

Read that again. decimal.TaxRate, item.IsFree, item.AsRupees() — every helper sits exactly where your brain expects it. New teammates can guess what they do without reading docs.

Shop price helpers in action

item.IsFree
item.AsRupees()
item.WithTax()
decimal.TaxRate

Steps

1

item.IsFree

instance property

2

item.AsRupees()

instance method

3

item.WithTax()

uses TaxRate

4

decimal.TaxRate

static property

Each call maps to a member in the extension block

Common beginner mistakes (and easy fixes)

Even simple features have small traps. Here are the ones students hit most.

Forgetting the receiver name for instance members. If you want to use the value inside (like price), you must give it a name: extension(decimal price). If you only write extension(decimal), you can only add static members, because there is no instance to talk about.

Using a property when you meant a method. A property should be cheap and feel like a fact. If your helper does real work (calls a database, loops a lot), make it a method with () so readers expect some effort.

Expecting it to change the type. It does not. You cannot add real state (fields) to someone else's object. Extension members are still helpers on the outside, like the clip-on handle. They compute from what is already there.

Name clashes. If the type already has a member with that name, the real member wins. Your extension is only used when nothing built-in matches. So pick clear, non-conflicting names.

Which member does C# pick when names match?

When should you use which style?

A simple rule helps. Use a property when the answer is a fact about the value and is cheap to get. Use a method when the helper does work or takes inputs. Use a static member when the helper belongs to the type as a whole, not to one value. Use an operator only for true math-like types where symbols read naturally.

If you are unsure, start with a method. Methods are the safest default, and you can always promote a cheap one to a property later.

A note on the wider .NET picture

C# 14 ships inside .NET 10, which is the current LTS (long-term support) release. LTS means it gets support for a longer window, so it is a solid pick for production apps you must maintain for years. Looking ahead, C# 15 and .NET 11 are in preview, bringing things like union types. But you do not need to wait. Extension members are ready to use today on .NET 10.

One friendly heads-up unrelated to syntax but useful for real projects: some popular libraries changed their licensing recently. MediatR and MassTransit are now commercially licensed for many uses. That has nothing to do with extension members, but if you are picking libraries for a new app, check the license terms before you commit.

Quick recap

  • An extension is like a clip-on handle: you add a helper to a type from the outside, without changing it.
  • C# 14 (in .NET 10) adds extension members, also called "extension everything."
  • You write an extension block: extension(receiver) { ... }, and the receiver is named once.
  • You can now add properties, static members, and operators, not just methods.
  • Name the receiver (extension(int n)) for instance members; leave it unnamed (extension(int)) for static members.
  • It compiles to plain static calls, so there is no run-time cost and the original type is untouched.
  • Old extension methods still work. No rewrite is forced; mix old and new freely.
  • Built-in members win over extensions when names clash, so choose clear names.

References and further reading

Related Posts