C# 12 is here, bringing a few fresh features.
It's not a big release. Still, it continues that functional line
of thinking that underpins the evolution of the C# programming language
through the last 10 or 15 years. Why functional design in C#?
Many programmers still don't get that point. So, stay with me, and I will try to clarify it.
I will explain why there has been so much stress on functional design and programming
in every single release of C#. There will be no coding demo in this video.
I want to explain the backing theory, the knowledge every C# programmer must
know long before writing any C# code. Let me tickle your imagination with a lovely
method utilizing expression-boded syntax. That looks like a lambda expression.
Oh, is this really a method? This could also be a local
function - did you think of that? That was a trick question.
Now, using a similar syntax, this time I assign a lambda to a
variable declared using the var keyword. A lot of new stuff here that we
only got in the last few years. The trick question: What is
the type of this variable, g? Come on, try your luck. Write your answer in the comment.
I would like to hear your thinking. What do you think, what is the
type of this variable, tell us! I'm asking because, before C# 10,
this assignment would fail to compile. Now it compiles fine.
Well, these two lines of code show C# today. Some will hate it.
Some will love it. And so, the question is: How did we
get here, how did C# become this? And let me remind you that you can also
join the growing community on my Patreon page where you can exchange thoughts about
demos, video courses and many other things. Come over there and join us.
Now the history of C#. C# 12, in particular, has
brought primary constructors. They make regular classes
start looking like records do. That will tease your mind if you
didn't accept records so far. There will be a lot of debate about primary
constructors in the upcoming months, I think. And I also believe that this is not
their final form we are seeing now. It will be an interesting period
before they become a mainstay in C#. Back to the previous version, C# 11, that is where
we we got more versatile pattern expressions. The list of supported pattern-matching
expressions in C# seems to know of no boundaries. C# 10 has also brought some improvements there.
It has also brought improved performance on records by introducing record structs that
literally render common structs obsolete. And that is when we also got a nice little feature
that passed under the radar, mostly: Lambda types. All right, that relates to that
question I asked in the introduction. What is the type of this variable, g?
This variable has a so-called natural type now. It will be the Func delegate type that
corresponds to the signature of the lambda assigned to the var variable.
That is a novelty in C#. C# is becoming quite dynamic
when it comes to lambdas. C# 9, oh that was a significant release.
Besides improving generics with covariant return types - do you know what that is? - we
also got init-only seters and, behold!, records. Any functional model is
unimaginable without records. They remove 90% of clog, of code, in large
portions of any functional domain model. Do you use records in everyday coding?
And don't tell me you're only using them as DTOs. That's not their purpose!
Do you use them for their intended purpose? That is the question for you.
Did you really adopt records? OK, back in time, C# 8,
another giant leap forward. That is when the nullable reference
types came into the picture. Nullable references, or as I always pointed
out non-nullable references, that is the point, their invert, their negation, non-nullable
references have brought an idea into many programmers' heads that they might write
code in which everything is an object. That was the new territory for
so many programmers of the time, and even today, unfortunately.
Thinking that you can write an entire model where everything is an
object and there is not a single null. That is also when pattern-matching expressions
started to attain the power we know today. And, before that, C# 7 brought one improvement
that mostly came under the radar: spans. They improved performance, they are very useful.
Anyway, C# 6, it cleaned some of the syntactic noise in common code with
null-conditional expressions and, now think: expression-bodied methods.
Well that is exactly when C# adopted a mainstream functional coding expression-boded
methods, methods that look like lambdas. Don't change things, make your classes immutable,
and implement methods that just return a value. C# 5 brought async and await, and C#
4 got covariance and contravariance. But only a few programmers at the time figured
that these features are actually helping write functional code and make functional designs.
They came after C# 3. Well, that was a big leap forward because
it was a game-changer in the direction of functional design, and C# never lost track
of that line, that functional design line. We got extension methods, lambda expressions,
expression trees - all the prerequisites to LINQ, which has forever changed the terrain
in .NET application development. We should remember C# 2 as well, which introduced
generics, and - voila! - here is the big picture. It shows what many programmers fail to see even
20 years after this process started, and that is that the most of the new features added to C#
and .NET are deeply in their hearts functional. They support functional
design and functional coding. I have already mentioned LINQ, which
is a first-class functional library. And another one coming with .NET
Core 1: immutable collections. That is how .NET and C# went hand in hand to
improve the ecosystem, to build a new ecosystem, in which we can successfully write applications,
business applications, that are mixing object-oriented with functional design.
Generics, they are a world in itself. Every piece of functional design is generic.
Generics must be your second nature if you plan to be a successful C#
programmer, it is as simple as that. Another large group is performance
upgrades that affect code. But these remaining features in between,
they are the essence of the modern C#. Extension methods and pattern-matching
expressions, those are the tools for defining behavior in a functional model.
And the rest, the rest is pure gold. Know this and you know C#.
These are the essential features in functional domain modeling
and they are all supported by C# today. I will skip generics and a few
other features in this video. Each of those is a significant theme in its own
right and I just cannot cover them in this video. I want to focus on this subset of features here
because not all features there are born equal. This large group on the left is
what we use when defining data. Think carefully, especially if you
are deeply entrenched in the old object-oriented thinking style.
We use these features to define elementary data structures.
These structures contain no behavior but only model the data.
Pattern-matching is another important aspect of modern C#.
You have seen that every recent version of C# has got more and more of pattern-matching expressions.
If you hate those cryptic little bastards, then think again: you might be wrong in judging them.
They will keep coming, more of them. They won't stop.
The rest is just syntax that glues all this together.
So, to draw a line under the last 15 years of C# evolution, this is the central theme
you see here: Separation of data and behavior. But why?
That separation is also the central pillar of functional programming as well.
It makes functional programming what it is. And now, it is a native resident, the
first-class citizen of C# as well. Here is why.
It is because of the way how we develop software today.
We don't just create an, I don't know, web application, with a SQL database under it.
That time is long gone. Today, we also develop other applications, other
services, potentially each having and owning its own storage, using a different storage technology.
But that, that means little. There are also services that
don't even have storage. They must talk to other services
and applications to get the data before spitting out their own output.
And more, there are third party APIs on the Internet we will connect to.
This picture shows the hidden truth behind application development today.
We write data in one shape, in one form, but then read it and process it in many other forms.
We should favor pattern matching when implementing all those numerous mappings.
Pattern-matching expressions are highly readable compared to procedural mapping
code and they're also almost always easier to write than the corresponding procedural code.
They help us reduce development time but also reduce the bug count by a large margin
compared to traditional coding styles. No wonder that C# has been improving its pattern
matching support in every version I can remember. Before farewell, let me show you
just a bit of a domain model I have developed in some imaginary application.
There's a whole bunch of immutable records here. Could those be classes, traditional
classes with methods and the state in them? Oh, wait a second!
You didn't hear it all. These data are not mine.
Part of them came from deserializing a response from another service.
With that data I form a request for yet another service which will
send me over the rest of the model. Do you see what I'm trying
to tell you all the time? The programming has changed and that
is why the C# language is changing, not because somebody wants it that way.
Most of the programming today is like: here's my JSON, give me your JSON, thank for the JSON.
How can you mutate the data you didn't even create?
How can you expect an object, an object that comes with behavior, if that object would be, the class
of that object, would be developed by another team, writing another service, which doesn't even
know that your service that consumes it exists? Let alone, how could it understand
the business needs of your service? That is the separation
between the data and behavior. That is where extension methods, lambdas and
everything related to that come to the picture, letting you define behavior specific
to your domain on the the data structures that you got from the other domain.
There are two ways for you from this point on. You can stick to the old-fashioned
object-oriented, pure object-oriented design, and hate it all the way through, suffer through
every line of code in modern applications. Or you can adopt the functional mindset and
start enjoying C# 12 as it is, as a mixture of object-oriented, functional,
and procedural coding and design, all supported in one language and one platform.
Now is the time for you to start coding, now that you understand what language you're using.
Subscribe to my channel and watch other videos. Every other video but this one is a coding
demo on my channel, so start watching them. Start from this demo, for example,
first, and enjoy using the latest C#.