Building C# 8.0
The next major version of C# is C# 8.0. It’s been in the works for quite some time, even as we built and shipped the minor releases C# 7.1, 7.2 and 7.3, and I’m quite excited about the new capabilities it will bring.
The current plan is that C# 8.0 will ship at the same time as .NET Core 3.0. However, the features will start to come alive with the previews of Visual Studio 2019 that we are working on. As those come out and you can start trying them out in earnest, we will provide a whole lot more detail about the individual features. The aim of this post is to give you an overview of what to expect, and a heads-up on where to expect it.
New features in C# 8.0
Here’s an overview of the most significant features slated for C# 8.0. There are a number of smaller improvements in the works as well, which will trickle out over the coming months.
Nullable reference types
The purpose of this feature is to help prevent the ubiquitous null reference exceptions that have riddled object-oriented programming for half a century now.
It stops you from putting null
into ordinary reference types such as string
– it makes those types non-nullable! It does so gently, with warnings, not errors. But on existing code there will be new warnings, so you have to opt in to using the feature (which you can do at the project, file or even source line level).
string s = null; // Warning: Assignment of null to non-nullable reference type
What if you do want null? Then you can use a nullable reference type, such as string?
:
string? s = null; // Ok
When you try to use a nullable reference, you need to check it for null first. The compiler analyzes the flow of your code to see if a null value could make it to where you use it:
void M(string? s)
{
Console.WriteLine(s.Length); // Warning: Possible null reference exception
if (s != null)
{
Console.WriteLine(s.Length); // Ok: You won't get here if s is null
}
}
The upshot is that C# lets you express your “nullable intent”, and warns you when you don’t abide by it.
Async streams
The async/await feature of C# 5.0 lets you consume (and produce) asynchronous results in straightforward code, without callbacks:
async Task<int> GetBigResultAsync()
{
var result = await GetResultAsync();
if (result > 20) return result;
else return -1;
}
It is not so helpful if you want to consume (or produce) continuous streams of results, such as you might get from an IoT device or a cloud service. Async streams are there for that.
We introduce IAsyncEnumerable<T>
, which is exactly what you’d expect; an asynchronous version of IEnumerable<T>
. The language lets you await foreach
over these to consume their elements, and yield return
to them to produce elements.
async IAsyncEnumerable<int> GetBigResultsAsync()
{
await foreach (var result in GetResultsAsync())
{
if (result > 20) yield return result;
}
}
Ranges and indices
We’re adding a type Index
, which can be used for indexing. You can create one from an int
that counts from the beginning, or with a prefix ^
operator that counts from the end:
Index i1 = 3; // number 3 from beginning
Index i2 = ^4; // number 4 from end
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"
We’re also introducing a Range
type, which consists of two Index
es, one for the start and one for the end, and can be written with a x..y
range expression. You can then index with a Range
in order to produce a slice:
var slice = a[i1..i2]; // { 3, 4, 5 }
Default implementations of interface members
Today, once you publish an interface it’s game over: you can’t add members to it without breaking all the existing implementers of it.
In C# 8.0 we let you provide a body for an interface member. Thus, if somebody doesn’t implement that member (perhaps because it wasn’t there yet when they wrote the code), they will just get the default implementation instead.
interface ILogger
{
void Log(LogLevel level, string message);
void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); // New overload
}
class ConsoleLogger : ILogger
{
public void Log(LogLevel level, string message) { ... }
// Log(Exception) gets default implementation
}
The ConsoleLogger
class doesn’t have to implement the Log(Exception)
overload of ILogger
, because it is declared with a default implementation. Now you can add new members to existing public interfaces as long as you provide a default implementation for existing implementors to use.
Recursive patterns
We’re allowing patterns to contain other patterns:
IEnumerable<string> GetEnrollees()
{
foreach (var p in People)
{
if (p is Student { Graduated: false, Name: string name }) yield return name;
}
}
The pattern Student { Graduated: false, Name: string name }
checks that the Person
is a Student
, then applies the constant pattern false
to their Graduated
property to see if they’re still enrolled, and the pattern string name
to their Name
property to get their name (if non-null). Thus, if p
is a Student
, has not graduated and has a non-null name, we yield return
that name.
Switch expressions
Switch statements with patterns are quite powerful in C# 7.0, but can be cumbersome to write. Switch expressions are a “lightweight” version, where all the cases are expressions:
var area = figure switch
{
Line _ => 0,
Rectangle r => r.Width * r.Height,
Circle c => Math.PI * c.Radius * c.Radius,
_ => throw new UnknownFigureException(figure)
};
Target-typed new-expressions
In many cases, when you’re creating a new object, the type is already given from context. In those situations we’ll let you omit the type:
Point[] ps = { new (1, 4), new (3,-2), new (9, 5) }; // all Points
The implementation of this feature was contributed by a member of the community, Alireza Habibi. Thank you!
Platform dependencies
Most of the C# 8.0 language features will run on any version of .NET. However, a few of them have platform dependencies.
Async streams, indexers and ranges all rely on new framework types that will be part of .NET Standard 2.1. As Immo describes in his post Announcing .NET Standard 2.1, .NET Core 3.0 as well as Xamarin, Unity and Mono will all implement .NET Standard 2.1, but .NET Framework 4.8 will not. This means that the types required to use these features won’t be available when you target C# 8.0 to .NET Framework 4.8.
As always, the C# compiler is quite lenient about the types it depends on. If it can find types with the right names and shapes, it is happy to target them.
Default interface member implementations rely on new runtime enhancements, and we will not make those in the .NET Runtime 4.8 either. So this feature simply will not work on .NET Framework 4.8 and on older versions of .NET.
The need to keep the runtime stable has prevented us from implementing new language features in it for more than a decade. With the side-by-side and open-source nature of the modern runtimes, we feel that we can responsibly evolve them again, and do language design with that in mind. Scott explained in his Update on .NET Core 3.0 and .NET Framework 4.8 that .NET Framework is going to see less innovation in the future, instead focusing on stability and reliability. Given that, we think it is better for it to miss out on some language features than for nobody to get them.
How can I learn more?
The C# language design process is open source, and takes place in the github.com/dotnet/csharplang) repo. It can be a bit overwhelming and chaotic if you don’t follow along regularly. The heartbeat of language design is the language design meetings, which are captured in the C# Language Design Notes.
About a year ago I wrote a post Introducing Nullable Reference Types in C#. It should still be an informative read.
You can also watch videos such as The future of C# from Microsoft Build 2018, or What’s Coming to C#? from .NET Conf 2018, which showcase several of the features.
Kathleen has a great post laying out the plans for Visual Basic in .NET Core 3.0.
As we start releasing the features as part of Visual Studio 2019 previews, we will also publish much more detail about the individual features.
Personally I can’t wait to get them into the hands of all of you!
Happy hacking,
Mads Torgersen, Design Lead for C#
Nice! Waiting to try out these new features using Visual Studio 2019.
This is good stuff! I’m assuming since it wasn’t mentioned, Records are not going to be in this release? If not, can you please provide an update regarding related topic/feature. Thx again and look forward to the new bits 🙂
It appears that record types (the one feature I really wanted aside from IAsyncEnumberable) did not make it 🙁
Records won’t likely make it into C# 8.0. I still want them in the language, but a lot of design questions came up, and we want to make sure we really nail this feature. Records are deeply intertwined with primary constructors, allowing object initializers on immutable types, virtual with’ers, discriminated unions, etc. Making it all work nicely with inheritance is a major challenge. So it just hasn’t all clicked together for us yet, but we’re working actively on it (https://github.com/dotnet/csharplang/blob/master/meetings/2018/LDM-2018-10-22.md).
Thanks for the reply Mads! Not happy Records are not going to make it this round, but understand. Like you mentioned, I also want to get the feature right. We’ve waiting this long… we can wait a little longer. Just provide feedback in the LDMs there are devs in the wild that would love to have this feature. Thanks 🙂
Forgive, my naiveness, but, what more do the Records bring to the table, than tuples?
Nice adoption of great F# features (explicit nullability, ranges, matching, switching on union types). Explicit nullability will transform C# usage for the better. Range looks like a good syntax. You will need an exhaustiveness check to complete the switch work (to remove the need for a default case).
So Index is 0-based when counting from the beginning and 1-based when counting from the end?
Yeah, noticed that. Kinda weird and not sure why that’s the case unless example is not accurate.
Python has similar practice.
You can get number of elements by subtracting total size with the two numbers.
Think of it as positive or negative indices. 0 is always 0, the first item in the collection. Indices prefixed with caret (^) are negative indices, counting back from 0, and -0 equals 0, which would be the first item in the collection again.
I personally would have preferred they use “-” instead of “^” for negative indices, because “-” is more explicit, and “^” is used elsewhere to indicate “from the beginning” (regex), but I haven’t read the discussion behind the design so I’m sure there are many reasons why they’re using “^” instead of “-” for negative indices.
We decided to follow Python when it comes to the from-beginning and from-end arithmetic.
0
designates the first element (as always), and^0
the “length’th” element, i.e. the one right off the end. That way you get a simple relationship, where an elements position from beginning plus its position from end equals the length. thex
in^x
is what you would have subtracted from the length if you’d done the math yourself.Why not use minus (
-
) instead of the new hat (^
) operator? This primarily has to do with ranges. Again in keeping with Python and most of the industry, we want our ranges to be inclusive at the beginning, exclusive at the end. What is the index you pass to say that a range should go all the way to the end? In C# the answer is simple: “x..^0
” goes fromx
to the end. In Python there is no explicit index you can give: “-0
” doesn’t work, because it is equal to 0, the first element! So in Python you have to leave the end index off completely to express a range that goes to the end: “x..
“. If the end of the range is computed, then you need to remember to have special logic in case it comes out to 0. As in “x..-y
“, where y was computed and came out to 0. This is a common nuisance and source of bugs.Finally, note that indices and ranges are first class types in .NET/C#. Their behavior is not tied to what they are applied to, or even to being used in an indexer. You can totally define your own indexer that takes Index and another one that takes Range – and we’re going to add such indexers to e.g.
Span
. But you can also have methods that take ranges, for instance.Thanks for the detailed explanation Mads!
This is an absolute gem of a response. I first thought that using ‘^’ instead of ‘-‘ felt…”off” but after the explanation, it now makes perfect sense. Because of this, it had me looking at the C# Guide / Language reference documentation; which now leads me to the suggestion that it would be nice to have a “Design Decision Summary” link in the ‘See also’ section. I have no clue where to post that suggestion…..and may continue looking as to where…
Thanks Mads!!!
So the comment in this peace of code:
Index i1 = 3; // number 3 from beginning
Index i2 = ^4; // number 4 from end
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($”{a[i1]}, {a[i2]}”); // “3, 6”
Should contain // “3, 5”, right?
No, ^0 would the element past 9, ^1 would be 9, so ^4 is 6.
Did you consider how DLang does it? They just defined that ‘$’ within slicing means lengthOf so you can do things like:
a[$-4] // to get 4th element from the end
a[5..$] // to get all elements from 5th to the end
a[2..$-2] // to get all elements from the second to the second last
Plus ‘$’ seems as a better choice than ‘^’ which is also XOR operator which is also used with number literals.
I would change the comment in the example: // number 4 from end
in: // number 4 (one right) from end, because otherwise it would read as 0-based, number 4 from end.
I was expecting the Records feature as well ….where is it ? :'(
Index counting from end, why not to use negative integer directly? I think it’s not necessary to introduce a new syntax ‘^’.
What if I need to represent a negative index (e.g. for index arithmetic)? It’s also not clear if this indexing syntax for arrays only or works with custom indexer properties (where negative indexes can be pretty welcome).
That would be a breaking change. Today, if you write someArray[-1], you’ll get an IndexOutOfRangeException and it needs to stay that way.
Indexing isn’t just for arrays. I can create a custom type with an indexer that uses negative numbers. In any case, Mads explained above the rationale.
What new runtime(CLR) features will come up with C# 8.0?
Could you write an article about that?
Would escape analysis come true? Would there be any GC improvements?
Mads,
When implementing these features, are you considering F# interoperability?
Yes. We have pretty regular conversations with the F# team on the features we’re each working on and how to get a good interop story between the languages.
Is there any online tool available where these features can be tried, without downloading VS 2019?
Yes, https://sharplab.io/. Choose the feature to play with using the dropdown at the top.
Oh, this is really good – thanks a lot!
Range and Index –
Index i1 = 3; // number 3 from beginning
Index i2 = ^4; // number 4 from end
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($”{a[i1]}, {a[i2]}”); // “3, 6” // Here i2 is printing 6
var slice = a[i1..i2]; // { 3, 4, 5 } // Here i2 is printing till 5
Shouldn’t it print 5 in both cases?
Normally range is left inclusive, right exclusive.
Lit bit confuse about the logic behind the following operaation.
Index i1 = 3; // number 3 from beginning
Index i2 = ^4; // number 4 from end
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($”{a[i1]}, {a[i2]}”); // “3, 6” — How 6 came to the picture ? “i1” is 3 from the beginning then array of a[i1] = 3 and a[i2] equal to 4 as per the given solution. But how 6 is possible ?
Because an Index of type ^4 goes from the end, so it’s the 4th character from the end (9 -> 8 -> 7 -> 6). It’s 1-based when counting from the end. For Range it’s 5 because its exclusive at the end, so a[i1..i2] results to a[3..6] = { 3, 4, 5 };
Range likes [3,6) so the result is {3,4,5}
Nice article! Looking forward to the new features. The Nullable Reference Types are specially interesting.
About the slice, var slice = a[i1..i2];, what is the type of slice? Can I use it to modify elements in the original array or is a new copy created?
Nice catch! We’re still debating what (if anything) slicing on arrays should return, so I glossed over it with a
var
. 😉Ideally, a “slicing indexer” (as in
a[x..y]
) would return the same type that it’s applied to, and that’s what the array “slicer” will do in the first preview, but that leads to copying. An alternative is to returnSpan
orMemory
, but that introduces types to mainstream code that are a little bit esoteric. Those would allow you to modify the existing array elements, though, which is nice. This is on the C# Language Design Meeting (LDM) agenda literally tomorrow.I hope you choose Span or Memory. There are already many hidden performance pitfalls in C# when used in blissful ignorance (*cough*, LINQ, *cough*).
Sure, newcomers will have to learn what Span/Memory is, but in my book that is a good thing. It’s not like a slice operator is the very first thing a new programmer would use and thus be confused by what it returns.
Also if `var` is used, an ignorant user doesn’t even have to know what is returned, at least for simple purposes.
It kind of sounds like the sliced object could be entirely contextual.
Declare it as T[], get back a new array with the copied values.
Declare it as ReadOnlySpan/ReadOnlyMemory, get a performant RO accessor.
Etc
As Span/Memory are a bit rough, what about using ArraySegment? It’s an old type which exactly represents a slice of an Array.
> “a “slicing indexer” (as in a[x..y]) would return the same type that it’s applied to, and that’s what the array “slicer” will do in the first preview, but that leads to copying.”
My two cents, but please no copying! I appreciate the problem raised, but I sure hope this can be done the performant route, with `Span` or `Memory`. Otherwise it feels like shooting this beautiful new feature in the foot, right off the runner’s block. Performance all the way :0)
Please no copying here, or you can just keep both two return type by adding a new operator, like this:
var k = arr[2..5]; //k is Span, which is ‘default’ return type for slice
var p = arr[2..*5]; //k is T[]
Or just inverse these two operator’s return type to make the ‘same type’ to be the default, but please give a way to obtain the Span directly.
The additional operator of course can be something else you like, ..+, ..=, **, etc, but please no triple dots, it’s hard to distinguish by naked eyes.
No need for new operators; you can use existing LINQ .ToArray() or .ToList() or an explicit new CollectionType(range) construction when you intend to make a copy.
I agree that slicing should not copy; that’s exactly what Span is for.
New features are awesome. But regarding to default implementation of interfaces, doesn’t it mean that we can have multiple inheritances from now on? How can it affect our current programming practices and years and years of design patterns that had the single inheritance as the main assumption? What’s the difference between interfaces and abstract classes then?
I have the same question. That interface is an abstract class…
Think of something like Max() on IEnumerable. You can write a default version of this that’d work on any type implementing that interface. Today you’d do that using an extension method. And you could have smarts in that method check for things like the IEnumerable being backed by a class like SortedList and, if so, be smart and just go to the last element.
However, that extension method is what needs to have that knowledge of all possible implementations to do efficient versions up front. Your own SortedAwesomeList class can’t benefit.
But with this new approach, IEnumerable can have a standard Max method which naively loops over every element, and then classes which are able to provide a very efficient Max() method can optionally do so.
Also, you can add new methods to an interface over time where perhaps, like above, they do some multiple steps that could be done using existing public contract of the interface, but with this new method you could have some classes do something smarter or different. Eg: you could have an existing T Get(K key) method that returns a single element, and then also add a default implemention of T[] GetMany(K[] keys) that just calls Get(key) for each key in a loop. That’d be horrible if each call resulted in a database lookup but fine if in memory. So your class that is backed by a database implementing this interface could provide a smarter array-based Get method that does one round-trip.
Ok but what if my IAwesomEnumerable already had a Max default function. And my SortedAwesomList implements both interfaces. Which default function will be used?
Try it, but I expect you will get an ambiguous member error and end up having to implicitly implement the method on one of the interfaces.
(just guessing here)
Then it would have to implement both interfaces using explicit interface implementation.
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/explicit-interface-implementation
You can implement multiple interfaces but only inherit from one class. So an interface is different from an abstract class.
Actually interfaces are still quite far from abstract classes. Classes don’t inherit members from interfaces, so if a class leaves a member
M
implemented by the interface, the class does not have a memberM
! It’s like an explicit implementation today; you have to convert to the interface in order to get at such members.ok, these interfaces on streroids are still different from abstract classes. What about the other way around: If default implementations are supported in interfaces, why would we ever want to use an abstract class then?
Because interfaces can’t have fields.
Default interface member implementations can be overridden by derived interfaces, which leads to a bit of a multiple inheritance problem: the so-called “diamond” problem where interfaces
I1
andI2
both inherit interface memberM
from interfaceI0
. Now a classC
implements bothI1
andI2
, and doesn’t provide its own implementation ofM
. What happens? The answer is that it’s ambiguous, and we give you an error.I suppose the fix is to implement M on the class, optionally delegating to an interface with ((I1)this).M()
But isn’t there still the problem that adding a new default implementation to an interface (the very reason this feature was added) may result in a breaking change in all classes that implement this interface and another derived from the same base interface? Granted, that doesn’t seem likely except for IEnumerable and similar hierarchies.
Adding a default implementation where a member is declared is never a breaking change. Adding an overriding default implementation in an inheriting interface can be: If another inheriting interface also has an overriding default implementation of the same inherited member, then a class that implements both interfaces and not the member will be broken. This feels quite unlikely in practice. Java has had this same semantics for a few years now, and it has not been an issue in real life.
I really cannot wait for the nullable reference types to become available. Will it be “visible” through reflection (i.e. will nullable reference types be marked with an attribute or something), so that analyzers can work on the assembly level also?
The nullable annotations will be carried in metadata with the help of attributes. So yes, the information is available, and analyzers (as well as the compiler!) can make use of it.
Fantastic. You are truly fixing the Billion Dollar Mistake.
Does that mean interfaces and abstract classes are similar now??? 😀
No. The difference is Interfaces can not define data fields. In other words interfaces can not affect memory space. Memory space is the building block of an object.
and what about nullable reference types and generics?
Constraints can be nullable, in which case the type argument is allowed to be nullable. Generic method bodies are checked conservatively, assuming that the type parameter may be either nullable (in which case you should check for null) or nonnullable (in which case the default value may be null). Type inference will infer nullability based on the null state of incoming expressions.
You may review the area calculation of circles 😉
Oops! Fixed. It’s been a while now since I went to school. 😉
Immo argued that most of the new features in .NET Standard 2.1 are for advanced scenarios, and (hopefully) library authors will be able to provide different versions of their libs for .NET Standard 2.1 and .NET FX Full.
That may be flawed in itself, but I think that the features
> Async streams, indexers and ranges all rely on new framework types that will be part of .NET Standard 2.1
don’t sound super advanced and “edge-casy”.
Isn’t that almost a guarantee that we’ll see heaps of code in the future that will no longer even _compile_ with/for the .NET Full Framework?
With things like System.Memory, there was at least that assured, even if the performance was not as
good as on .NET Core.
Regarding
> As always, the C# compiler is quite lenient about the types it depends on. If it can find types with the right names and shapes, it is
> happy to target them.
Is that a hint that we’ll see some way to make such new code at least functionally work on .NET Full Framework.
Almost forcing a divide between the platforms here sounds really dangerous and IMHO rather unnecessary.
If it is “just” types that are required in the framework why not add them to make things just work – regardless
of whether the whole .NET 4.8 is considered .NET Standard 2.1-compliant or not?
I totaly agree. Code which does not compile with full Fw sounds horrible.
In case of the subtext of the recent posts by MS weren’t clear- .NET Framework is now deprecated. It will not be receiving new features outside of minor bug fixes and security updates.
Yes, it will be a big pain to migrate all the existing code out there, but you can’t say that this hasn’t been telegraphed for years now.
Totally agree. We have a mix of ASP.NET Core applications and classic ASP.NET applications–with a non-trivial amount of code in WebForms. The closest thing to a migration path there was the promise of NetStandard and the introduction of decent DI in webforms 4.7.2. That these new features are Core-only means any version of NetStandard > 2.0 and any version of C# >= 8.0 are effectively off limits to us. Fast forward a few years and it’s going to feel like the sliver of .NET Core and C# that we can benefit from is a legacy platform. I know webforms is dead for a long time now, but it’s not going anywhere anytime soon. You threw WPF and WinForms apps a bone with .NET Core 3.0. You really need to do the same for the classic ASP.NET stack before pulling a move like this. Library authors should not have to avoid language features because some of their consumers are on the .NET Framework.
I don’t see how your statement is different from “we’re using .NET framework 2.0, why can’t we use those features from .NET framework 4 ?” The answer has always been “you must upgrade” when a new major version of the framework rolls around.
In a sense it’s the same with Core. I don’t know about the long-term support story, which might be a reason to stick to the full framework, but your case sounds more like “we don’t want to upgrade these”, which wouldn’t be any different if a new major framework version came out and you’d try to mix v4 and v5 apps.
I’m reconciling myself with the implications of all these announcements, now, but there is still a qualitative difference here. The difference is that even major releases of .Net Framework don’t drop large chunks of the BCL/frameworks. There are many features missing in .Net Core that prevent migration.
The reason the compatibility bar is so high for new releases of .Net Framework is that Microsoft’s customers PLACE HUGE VALUE on that compatibility.
For the most part, framework upgrades have been additive and straightforward to opt in to. The migration path from .NET to .NET Core, specifically ASP.NET to ASP.NET Core has not been. It wasn’t practically possible to share libraries until netstandard1.6. If you depended on (virtually) any third party packages, it was really netstandard2.0. The only option for modernizing a very large code base with legacy code was pulling business logic out into libraries, rewriting code that can’t be ported (asmx, wcf, etc.), and rewrite the UI layer gradually. We could integrate Microsoft.Extensions.* into the classic stack and use the logging & DI abstractions in those libraries so we can be backward and forward compatible.
I was hoping MS would allow us to one day host ASP.NET Core inside of a classic ASP.NET application (brige OWIN) so we could modernize in-process, but that ship sailed last week when ASP.NET Core announced it would go .NET Core only. Alternatively? Port WebForms to ASP.NET Core but it’s clearly not going to happen (though shockingly WinForms made the cut?!). If MS is waiting for a build conference to announce that it’s going to happen after all, I’d urge you to reconsider the marketing strategy!
At some point, the Microsoft.Extensions packages will want to use C# >= 8.0 features and then we’ll be in this situation: instead of modernizing our application, larger companies will be able to get to 2018/2019’s modern… for the years it will take to get there. Hopefully/doubtfully the 2.x branches will continue to get updates. Then we’ll have to migrate again to .NET Core 5.0 or 6.0. For most companies in this predicament, a wholesale rewrite isn’t practical or possible. netstandard2.0 opened a practical path forward. netstandard 2.1 w/o .NET Framework support, C# 8, .NET Core 3.0, and ASP.NET Core 3.0 seem to be reversing course on that.
> Immo argued that most of the new features in .NET Standard 2.1 are for advanced scenarios, and (hopefully) library authors will be able to provide different versions of their libs for .NET Standard 2.1 and .NET FX Full.
That is true for the majority of the APIs added in .NET Standard 2.1. But as I said, it also contains a bunch of minor impromevements across the board (such as HashCode and Dictionary.TryAdd). While these APIs are useful, they are hardly required.
However, I agree with you that this is more impactful for exachange types, such as Index, Range, and IAsyncEnumerable. But even though these types appear small on the surface both Index and Range require runtime changes in order to be supported on arrays.
> Isn’t that almost a guarantee that we’ll see heaps of code in the future that will no longer even _compile_ with/for the .NET Full Framework?
In a sense yes. We’ve made it clear that we don’t plan on bringin most of the new features to .NET Framework anymore. But that’s not entirely different from .NET Framework version X to X + Y. The expectation is that most of the applications that are actively evolved will eventually be ported to .NET Core. Of course, this won’t happen over night. But keep in mind that many of our existing .NET Framework customers aren’t even targeting the latest version of .NET Framework either. It’s going to be a transition that will happen over multiple years. And we’re still working on making that transition smoother in tooling as well as in us bringing more APIs to .NET Core.
> Almost forcing a divide between the platforms here sounds really dangerous and IMHO rather unnecessary. If it is “just” types that are required in the framework why not add them to make things just work – regardless
of whether the whole .NET 4.8 is considered .NET Standard 2.1-compliant or not?
That is an option. But as I explained the primary reason is we no longer default to make all features available on .NET Framework. We might decided to add some features to it, but it will be scrutinized because the compat bar is very high and the only way we can keep it by minimizing churn. And as I said, even “innocent” types like Index and Range require runtime changes due to array support. That doesn’t mean we can’t fix it, but it’s certainly not as easy as just copy & pasting some .cs files over either.
I always perceived the .NET Standard as way to reconcile the .NET Framework and .NET Core. If the .NET Framework stops implementing new .NET Standards, what is the point of this standard?
Immo I think that ignores the reality that going from .Net 4 to .Net Core loses a lot of functionality today. It isn’t practical to move to .Net Core for many programs.
Similar to EF Core, it feels like .Net Core is far from ready to be a full time replacement for full .Net, but MS is pushing it anyway. So in the future there will be no forward migration path for .Net 4 users, which seems like the death knell for .Net.
I have some question about the nullable references:
– Will there be a option to turn those warnings for nullable references into errors?
– Is it possible to determine if a type is nullable or not using reflection? This could be very useful in IOC containers as you could check if a argument is ‘optional’ or not.
– Warn as errors should continue to work also on those new warnings.
– We’re still working out what is exposed how through reflection. That said, while the nullable info is in metadata (see my previous response above), the runtime doesn’t know about nullability, so I am not sure whether we can show it at e.g. the
MethodInfo
level. We’ll see where that lands.I love the async/await foreach and the Index changes .
I’m sad, that it will not work witht the .NET Framework, though. 🙁
Hi, great article! Just want to say the area of a circle is pi * r^2 (pi * r * r), so the example should be c.Radius * c.Radius * Math.PI (shame that C# does not have the “power” operator like python to do Math.PI * c.Radius ** 2)
Where’s Extension Everything ( https://adrientorris.github.io/csharp/what-i-get-from-the-preview-of-csharp-8-with-Mads-Torgersen.html#extension_everything )?
I was really hoping for that feature…
Extension everything is my top wish for C#9. Extension properties especially would be useful for so many scenarios.
C# 9? Wasn’t that feature scheduled to come on C# 8?! =(
Extension everything didn’t make it into C# 8.0. It got “caught up”, if you will, in a very exciting debate about the further future of the language, and now we want to make sure we don’t add it in a way that inhibits those future possibilities. Sometimes language design is a very long game!
Very interesting debate indeed… I understand… Thank you for the reply.
Nice features ! However I am also wondering about records. I was expecting them to be released in C#8.
Great set of features, especially love async enumerable!
However, I’m still disappointed with that nullable reference types remains basically just a static analysis tool, after a year of discussions.
This feature does allow me to make less mistakes, but doesn’t help me to write safer code. I still have to add null checks manually where I expect external (e.g. relatively to my assembly) code calls. So I wan’t to ask the same question I asked year ago: “How about at least adding a compiler switch to enable preconditions generation for public APIs based on this non-nullability support?”
Another question I have: is this new indexing syntax for arrays only or works with custom indexer properties?
Thanks.
– Preconditions: We’re looking at a feature where you can annotate a parameter to automatically generate a null-check for it. This is still on the docket for C# 8.0, and I hope it makes it.
– Custom indexers: Yes, the index and range support works wherever you like it to, including in your own custom indexers.
Thank you for the answers! On preconditions, I would love to see at least that kind of null checks. I hope that will be done via some language syntax (like ‘!’) and not by manually specifying some custom attribute.
We’re adding default methods to interfaces, so that we can preserve backwards compatibility when making a new version of an interface. Backwards compatibility is critical to us here at Microsoft.
NOTE: This feature is not backwards compatible with any .NET code currently in existence.
🙂
Ouch! Fair point. 😀
This feature depends on a new version of the runtime itself. Give it a few years, and it should become wide-spread, especially given that .NET Core etc. are side-by-side capable.
True. The idea is that default interface implementations lay the foundation so that future versions of the runtimes supporting it can be backwards compatible with interface changes.
Great changes… love them.
Except one….
.NET Standard 2.1 is a BAD naming number. The whole .NET Standard 2.0 is confusing at best. Would you just rename .NET Standard 2.1 -> 3.0 and remove some of the headaches you’ve created with this .NET Standard business?
I realize that maybe there is a business reason for it, but it seems really silly, at this point, to NOT map your Standard naming to Core.
Hah, the nullable reference types is a bit controversial and it will surprise a lot of folks! Somewhat we are already used to managing the null references and with that feature now the code will be more “loose”.
I really think nullable reference types is a weird name for this feature.
I mean, I think I get what you are going for, similar to nullable value types, but reference types can already be null by default.
What happens if you use string? but never turn on the compiler switch to warn about nulls in normal ref types?
The current plan is to give you a warning if you use
string?
in a place where the feature isn’t turned on. That way you get reminded to either turn it on, if that’s what you meant to do, or to remove an unintended?
.Interfaces with default implementation are just abstract base classes.
Interfaces never contain implementation – that’s the reason for having interfaces invented: To just share contracts.
I don’t like to idea to leave that path. Particularly not in regard to web services.
That’s not true, though whether the differences are worth it or they should have just gone for full multiple inheritance is hard to say.
Interfaces don’t add properties automatically, and abstract base classes can add fields.
That in itself raises an interesting point; this still won’t let you add a new property to an interface without requiring the implementing classes to change.
This is probably not a bad thing conceptually, but I wonder if it will lead to a proliferation of GetX() methods instead of properties in the future…
> So this feature simply will not work on .NET Framework 4.8 and on older versions of .NET.
What does it mean exactly? If a .NET Framework application references a library that uses default interface methods, what will happen?
I believe it won’t be able to reference, since that feature will only exist starting from .Net Standard 2.1, which won’t be supported by .NET Framework 4.8, thus no referencing.
The compiler won’t let you compile with default methods on interfaces unless it can find a the platform type RuntimeFeatures and that one has a field called DefaultImplementationsOfInterfaces. See this CoreFx issue for details on the design: https://github.com/dotnet/corefx/issues/20157
It basically means RuntimeFeatures allows us to track runtime type system features like any other API. This also works for .NET Standard.
Will the attributes for methods that include null checks make it in the 8.0 release? I am talking about these
https://github.com/dotnet/roslyn/issues/26761
https://github.com/dotnet/roslyn/pull/26656
I care very deeply about these since without them I think non-nullable reference types are unusable. What is milestone 16.0 I hope it is not C# 16.0 🙂
We plan to have a set of attributes that you can use to mark members and parameters that have “special behavior” around nullability. The work is not quite finalized yet, but it will be part of C# 8.0.
Awesome updates!!! I also like the idea of the new indexing! By the way, is there a possibility of (object-based) null/presence check? Let us say, you have a variable named ‘person’ of type (class Person). Can we do check like this ‘if (person)’ or ‘if (!person)’? The equality should be derived on the ‘default()’, in this case it is ‘default(Person)’. If the target object is value-type, then it should be equaled to the default value of the value-type. This seems to work like in JS.
We have no plans for this kind of shorthand. It was deliberately left out of C# 1.0 to catch a whole class of errors that are common in C (e.g. accidentally writing
x = null
instead ofx == null
). I think the benefits still outweigh the slight discomfort of having to write out the Boolean expression.With extensions anywhere, could a generic conversion to bool be written to add this feature?
I’m really excited about IAsyncEnumberable, but it doesn’t require language support to be useful, you could produce and consume it with extension methods and higher order functions.
I think it’s really important the there are canonical implementations for netstandard2.0 and lower, and full framework. Could the Ix.NET library be the place for this? Or something shipped by the C# team?
Agreed. If async streams will work fine on .NetFx 4.8 as long as the types are available, then it will be important to document clearly where best to get a “good” version of those types.
I see this as similar to the way the BCL shipped with IObservable & IObserver in the box, but not all the Rx framework implementation. I guess the requirement for ValueTask makes it impossible to put IAsyncObservable in the BCL, but clear guidance on where to get it (like there currently is for ValueTuple) is both practically useful AND an important message to NetFx developers that we’re not being ignored.
BTW: stronger support for Rx/Ix.Net in docs & examples would be nice. All other languages seem to be pushing their ports of Reactive Framework (Rx) much more strongly than .Net, even though .Net had it first!
P.S. Sorry if all my line breaks get lost. It always seems to happen to me on here. Not sure why.
is there a way to test those new features without VS 2019 ? especially simply using net core under linux ?
Please port ASP.NET Web Forms to .NET Core now that you want to hobble .NET Framework. I don’t see what the problem is making Web Forms work on .NET Core.
I suspect its not a technical issue, rather “snob value”. Ten years ago I had colleagues who wouldn’t hire a VB developer because “vb developers dont know how to write decent code” it had to be C#. Now its “webforms developers dont know how to build web sites” – it has to be MVC.
A pity, I know, but this industry is full of fashionistas, once again I have to say “customers REALLY dont care if you’re using DI/AOP/MVC/Agile/Kanban/[insert acronym here]” they just want something that performs a business function.
Agree at all
What about extensions for everything? It was talked about a lot but I guess we won’t see it in 8.0?
This. Though I am afraid it would only come to .Net Core now…
Uhm, I just asked whether it would come in C# 8, and it should be the same C# 8 for .Net Framework and Core, right?
Mads posted a link to an explanation, earlier in this “discussion”, of why Extensions Everywhere didn’t make it. Here is the link:
https://github.com/dotnet/csharplang/issues/1711
Are you also planning to change LINQ extension methods to default interfaces?
So you would have default implementation of Count() on IEnumerable and ICollection and can get rid of for type checking “ifs” in current implementation. Do you this approach could be more performant?
Would ‘fixed buffers’ and ‘params span’ come into C# 8.0?
I hope to have ‘Indexed property’ in C# 8.0, is it in plan?
“Default implementations of interface members”
Wait… What? 😀 So maybe next feature will be “Constructor for interface”. Lady and Gentleman, welcome Abstract Class with a brand-new name: Interface.
Seriously, I can see this feature being misused in place of abstract class rather than fixing the “Today, once you publish an interface it’s game over: you can’t add members to it without breaking all the existing implementers of it.” issue.
It’s good you added making ordinary reference types nullable but assigning null to a non nullable reference type and allowing you to compile your code so you get a runtime error instead of compile error? That one I don’t understand. I don’t even think any language that supports nullable reference types work like that. Kotlin from documentation seem to give a compile error https://kotlinlang.org/docs/reference/null-safety.html
It’s hard to add this kind of feature to an existing language and maintain compatibility. We opted to do it in a way that has zero effect on runtime semantics, and only impacts diagnostics out of the compiler. If you want to turn on warnings as errors, though, that should give you what you want.
What will happen if I write nullable reference types in C# and use it in VB?
C#
public delegate bool NullableStringPredicate(string? text);
public delegate bool StringPredicate(string text);
public void DoWorkNullable(NullableStringPredicate predicate)
{ … }
public void DoWork(StringPredicate predicate)
{ … }
VB
DoWorkNullable(Function(text) Len(text) > 0) ‘ Should not have warning here, because Len(vbNullString) returns 0.
DoWorkNullable(Function(text) text.Length > 0) ‘ Will the VB compiler generate a warning here?
DoWork(Function(text) text.Length > 0) ‘ No warning, because `text` is not nullable.
VB – as well as older versions of C# – will not understand the attributes that the nullable feature puts into assemblies, so they will just ignore them. This is just one way that you can miss out on nullable warnings, and thus one of the reasons why the nullable feature cannot provide absolute guarantees.
Great work guys.
Especially with the switch expressions. It’s a gem in the Kotlin/Swift worlds and will be very welcome in C#.
Question about target-typed expressions: do these work for simple non-array instantiation too?
it: Instead of the current ‘var thing = new Thing( x, y )’
will it work as: ‘Thing thing = new( x, y )’?
This could be an interesting direction for the language conventions to take if so.
That’s exactly right. “
Thing thing = new (x, y);
” will work the same as “var thing = new Thing(x,y);
“.Oh great. Towards ever more obtuse programming.
Yes, yes, YES!!!
Default implementation of interface members? Are you kidding me??? PUKE! Yes, let’s muck up our interface declarations now with implementation details. Just another unnecessary change to make things more messy and complicated. Let’s hope this feature goes the way of primary constructors in C# 6.0.
I was thinking that too, the default interface is a monkey wrench 😂
Like any tool/wrench/monkey??… it’s about how you use these.
All of Kotlin, Objective-C and Swift and have these and they are tremendously useful, especially to allow the use of ‘optional methods’ of interfaces. So for example the default implementation does nothing (probably the most common use case of default implementations) and implementors only need to override methods they want to actually add functionality in for.
This is used extremely successfully throughout UI frameworks (such as both Cocoa and Android), and can be especially useful for API authors that want to allow extensibility but without having to clutter your class with a bunch of empty methods just because you want to implement one.
A few years from now you will be thankful for how API authors will be able to provide more expressive building blocks that don’t overly pollute your code – and it will be because of default interfaces that they will be able to do so.
It would be really nice to have some kind of automatic conversion feature for the Nullable reference types so that reference type variable declarations in legacy code _automatically_ by default get the ‘?’ suffix so that at least legacy code will compile and run properly with C# 8.0.
Then afterwards, bit by bit, code can be cleaned-up and refined by removing the nullable ‘?’ suffix in cases where this makes sense.
Without this automatic conversion feature big legacy projects cannot be easily be upgraded to c# 8.0 – manually going through all the code in adjust the reference type declarations for just the c# upgrade would be a lot of work.
I agree. If you’re interested in this feature, when would you NOT want it to be in effect? Seems like it’s a feature that either you would never care about or would want everywhere in your code. In that case, why not make it a compiler/build setting (as opposed to syntax change) so that you can turn it on/off wholesale without affecting current code?
Not all variables in a code base are equal. Some are meant to be null sometimes, and some are not. We need a syntax to let the developer express this intent. Either it is nonnullable, and then we will warn you when you put null in it. Or it is nullable, and then we will warn you when you dereference without checking first. Those two meanings are both different from what we have today, where we don’t warn you about anything null-related. Therefore there is also a compiler switch to turn on the null warnings to begin with – so we don’t just start breaking your old code with new warnings.
instead of Point[] ps = { new (1, 4), new (3,-2), new (9, 5) };
Why do you not make more shorter like bellow?
Point[] ps = { (1, 4), (3,-2), (9, 5) };
If “new Point(1,3)” is unnecessary, new keyword is also unnecessary as there must need an object which will be initiate if not created before.
Your shorter syntax already means implicit conversion from a tuple type.
public class C {
Point[] ps = { (1, 4), (3,-2), (9, 5) };
}
public struct Point {
public Point(int x, int y) {}
public static implicit operator Point((int, int) tuple)
{
return new Point(tuple.Item1, tuple.Item2);
}
}
Can those new language features made available to .net Framework 4.8 through nuget packages?
Like others I was also confused by the Index type.
I’m not a native English speaker, but it seems to me that the problem lies with the “number 3 from beginning” comment? Perhaps it should be rephrased to not rely on a programmatic context?
Take a queue of people – Sarah, Mike, Yoda, Hu and Chloe – lining up to enter a door, with Sarah being first in line. Say “number 3 from the beginning, please take off your hat”, who would take off his/her hat? I’m not 100% sure on the English language right there, but the answer to that question should not differ between the context of my queue-example and the context of the Index type example. If it does, the phrasing in the “number 3 from beginning” comment should be somehow changed to match real-world English (and not rely on an implicit context).
It’s not a big deal right here right now, but my guess is that this example and wording will be re-used in other places.
The switch case change has been a long time coming. I’m wondering if the LINQ Where statement can use that Async for list searching?
I’m felling kind of lost about all this. Too many frameworks, language versions, dependencies etc. Too often you get to hear: you need to migrate this to that, that to this etc. Sorry. SW development is for business value – not for joy of “beautiful” code. I guess very few will take an existing ASP.NET application tageting .NET 4.x and try to port it to ASP.NET Core so they are able to use some nice new C# 8 features. For me the .NET eco-system has become unstable and unpredicable. WPF – kinda dead, who knows? WebForms – who knows. Appears to be deprecated but still at the core of SharePoint ? “Classic” .NET – appears to be dead-end road – is it? .NET Core – far aways from mature. OLE DB – dead – not dead – still dead? A developer has to invest a large effort in keeping up-to-date about what works where and how technically instead of being able to focus on creating business value. It get’s disctracting.
But something as productive as “Edit and Continue doesn’t work in VS 2017. No wonder the web developers use PHP or JavaScript/TypeScript/HTML
If you forget about Windows and .NET Framework stuff, everything will make sense again. The future of .NET is .NET Core, unfortunately .NET Framework is and will be the past of .NET, tied to Windows. If you still have a lot of investments on Windows and .NET Framework, well, it’s time to consider if you want to jump to the new tools and innovations, or wait until the end of support for .NET Framework and Windows is reached. You listed several products that may be legacy in the future, such as OLE DB, WPF, WinForms, ASP.NET WebForms, SharePoint; it’s a matter of time Microsoft ignore those or migrate them to .NET Core (SharePoint running over Linux would be nice to have). .NET web developers are following the trend, that is, ASP.NET Core, using MVC, Web API, etc., giving you a lot of control over your HTML and JavaScript content. TypeScript is driven by Microsoft as well. By the way, you can build an Web Progressive App (using JavaScript and HTML) and running on the desktop in Windows 10. It’s up to you to continue in the past, or go ahead and embrace .NET Core. You still have time to think about it, you will get .NET Framework 4.8 and .NET Core 3.0 next year to play and test.
One thing I disagree on, is that I don’t think SharePoint is going to run on linux any time soon. More likely, it’s going to be a SaaS only service, with the on-prem version discontinued at some point. Already many features of the SaaS version don’t reach the on-prem one.
How is Default interface member implementations any different then an abstract class?
Abstract class can be a part of the inheritance hierarchy. (e.g Object). Class does not support multiple class inheritance.
Interface can be used to form code-contract hierarchy. Class supports multiple code-contracts implementation.
I find quite hard to implement nullable reference types into an existing project code base.
Why not take a more conservative approach using e.g. the ! (bang) operator after a reference type to denote non-nullability?
I can write
` string middleName = null //existing functionality
string! firstName = “Theodore” //must not be null
`
That would only be half the feature: We would guard non-null references against null, but once you’re nullable you’re on your own, just like today. We wanted a feature where nullable reference types are also protected against being dereferenced without a null check.
Given:
Index i = n; // where n is a positive integer
Can I later ‘flip’ i so that it instead counts from the end? i = ^i; // valid syntax?
Not currently. Would that be useful?
Seems like you could i = ^i.Value; ?
I prefer to add inline check for null, in the second sample:
void M(string? s)
{
Console.WriteLine(s.Length); // Warning: Possible null reference exception
if (s != null)
{
Console.WriteLine(s.Length); // Ok: You won’t get here if s is null
}
}
to be:
void M(string? s)
{
Console.WriteLine(s?.Length ?? 0);
}
The nullable flow analysis will work just as well for that too. The idea is that we will try to recognize whichever way you chose to check for null.
Hi,
Anyone knows what happened to the “viable alternative” of IAsyncEnumerator, mentioned here: https://github.com/dotnet/csharplang/blob/master/proposals/async-streams.md with interface like this:
public interface IAsyncEnumerator : IAsyncDisposable
{
ValueTask WaitForNextAsync();
T TryGetNext(out bool success);
}
The link says quite correctly that this would enable tight looping over the (possibly buffered) IAsyncEnumerator. I personally was quite intrigued by this API.
Is it possible that maybe Roslyn will support both variations in the future?
This was settled in the C# design meeting on Oct 3. Please see the meeting notes here: https://github.com/dotnet/csharplang/blob/master/meetings/2018/LDM-2018-10-03.md
Thanks for the link! After reading it, it is nice to know that the faster (but a bit more clumsier) interface is kept in the “back pocket”, and is not a dead idea.
About async LINQ – I am not sure what is decided by you guys yet, and what is opened. What do you think of the following idea: the static extension methods (eg “public static IAsyncEnumerable Where(this IAsyncEnumerable enumerable, Func predicate)”) should be call-through to some “AsyncProvider” interface of this “IAsyncEnumerable”? This would enable the producer of IAsyncEnumerable (typically library implementing some binary application-specific protocol) to be able to fully control of even the LINQ-based control flow of the IAsyncEnumerable it returns.
This way, it is possible to create LINQ-based system, where you can write queries using LINQ syntax, and they could be run remotely and possibly in parallel (think of this as LINQ-based Apache Spark). That requires serialization of IL of the callbacks given to LINQ extension methods, but that is not impossible, just challenging.
LINQ to SQL (System.Data.Linq), Entity Framework, and Entity Framework Core already support running LINQ queries remotely. They are based on System.Linq.IQueryable, which uses lambda expressions instead of serializing IL. Entity Framework and Entity Framework Core even support asynchronous queries, and I suspect that asynchronous enumeration could be added as a few extension methods without needing any IAsyncQueryable. However, the restriction against multiple parallel operations in the same DbContext might become too easy to violate by mistake, then.
I just want to make sure I’m clear on how these new features will be released in future versions of the .Net Framework: If I’m reading this post correctly, are you saying that all new C# language features will be in .Net Core? What about references to the Microsoft.Net.Compilers NuGet packages…will this enable project support for these features? As you can imagine, porting .Net “classic” (as we’re beginning to call it internally) code over to .Net Core isn’t a trivial task, but our teams will greatly benefit from these features…can we reference the .Net compilers packages and get the language features? If not, this seems like a HUGE oversight on the part of Microsoft. If there is another way to get these features I’m all ears as well.
Keep up the great work!
Looks good! Nullable reference types maybe finally can give us stable software 🙂
Little bit confused with the Index feature, in my understanding,
Index i1 = 3; implies 3rd number from beginning that is 2 and
Index i2 = ^4; implies 4th number from end that is 6, am I right?
please clarify
Count from beginning starts with 0, as to be expected. Like some other languages, indexes mark points between values, or to the left of values. So index 0 is 0, index 1 is 1, etc. OTOH, index ^0 is past the 9, index ^1 is 9, so index ^4 is 6. However, ranges specify the values in-between the endpoints, or are inclusive for the beginning, and exclusive for the end (hence the need for ^0).
Switch expressions are great but why using underscore instead of ‘default’? IMHO it will be less readable (harder to spot at first sight).
The underscore comes from F# (and other functional programming languages). The direction of C# is heavily influenced by F# these days, and that’s a good thing. The C# language design and compiler teams would obviously like to move faster on this, and C# users like me would love to see record types, discriminated unions, exhaustive pattern matching and the like make it into C# (looks like we’re getting part of the way there with the new switch expressions). But, given C#’s OO background, it’s a big ask because it’s difficult to both design and implement these features such that everything in the language (everything!) works nicely and interops together and gels as a whole. Mads alludes to this above wrt records, and how they are deeply intertwined with all sorts of other language features. As a user, I might not care that records are deeply intertwined with feature XYZ (if I don’t use that feature), but the team has to care. Frankly, C# has done an amazing job to date with staying relevant, and taking on useful functional features in a controlled and nicely designed way. It remains my favourite language.
For the target-new expression, is it possible to omit the new keyword to make more readable:
Point[] ps = { (1, 4), (3,-2), (9, 5) };
instead of:
Point[] ps = { new (1, 4), new (3,-2), new (9, 5) }; // all Points
I think we can apply the new operation to (EXPR…) assignments implicitly also.
For the Index type, can index be negative? If not could we use -1 (not ^1) for the last element? As the character ‘^’ looks like an operator.
I’m sorry Mads but you don’t convince me.
Records have been pulled out of C# specifications since 7.0.
And when C# 7 was in a stage before official release, reason why it was Records were removed weren’t technical but it was perceived “too functional”.
So if it were like that at that time, now it hasn’t changed.
And in 2018 we are still forced to wrap return types because ValueTuple are not enough and we don’t have Records yet.
I mean…c’mon,seriously?
And F# can do that.
Why do I need reference types as nullable !!?? By default they are null, ain’t they?
Yes, reference types are already nullable. This new feature is providing compile time checking to help prevent NullReferenceExceptions:
string x = “someString”
// The two line after this comment will be a compile warning.
// Prior to C#8, you get no compile warning and get a runtime NullReferenceException.
// C#8 will still give you a runtime NullReferenceException
// but if you pay attention to compile warnings and fix them, the problem can be fixed and
// your testers or customers will not get a NullReferenceException
x = null;
This will be very beneficial when calling methods and the return type or a parameter is marked as nullable.
If a return type is marked as nullable, you know you have to check for null before accessing it. If it is marked as not nullable
then you should be able to feel safe to not have to check for null when accessing it.
// The next line tells the compiler you expect x to allow null and it won’t give you a warning.
string x? = null;
Ranges and indices: why for int[] aa = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } not use aa[0..0] to produce slice with all elements included, so aa[1..-1] will be slice without the 0 and the last elements of the array, and so on.
Maybe also worth to add aa[1:3] meaning, from element 1 take 3 elements – like aa.Skip(1).Take(3).ToSlice() or aa.Skip(1).Take(3).ToArray().
Maybe copy or not copy array values to slice, should be somehow marked by ref, i.e.: bb = aa[1..5] versus ref bb = ref aa[1..5] .
Fantastic! I hope all these new C# features are inspiration for F# to to through a few new growth spurts now that C# is has done such a great job of getting caught-up to become a bit more expression-orientated – and the support for “traits” is great too.
As F# does, I’d love to see C# make the superfluous “new” keyword optional in favor of naked constructor calls in the general case, and support for expressing enforced DU’s now that C# has the pattern-matching to support it.
I had expected C# to also tackle type providers, if not provide an even more flexible meta-programming model though a combination of the compiler and tooling — is something like this in the works?
And to echo others here:
– Please – slices use Span by default w/o copying
– Records – yes…
As for records, ISTM that with traits you could do this like so:
class MyRec : IStructEquatable { …lot’s of {get; set’s…}
Then a default version of IStructEquatable could do the right thing?
But for this to work well:
– You’d need to be able to emit performant (non-reflection-based) structural equality code fro the implementation
– You really want to be able to have interfaces specify operator overloads, such as operator==, etc.
I suppose one can define POCO records from F# (POFO’s?) and then consume them on the C# side for the moment.
What about class constructors with dependency injections – why repeat 4x the same variable names in simple cases, why not use syntax from TypeScript.
Example: class MyClass
{
private readonly IMyVar _myVar;
public MyClass(IMyVar myVar)
{
_myVar = myVar;
}
…
}
we can use something like:
class MyClass
{
public MyClass(var IMyVar _myVar){}
…
}
I am exiting to use Async streams when it comes in VS 2019
We can use the extension method instead of default implementations of interface members. Maybe, default implementations will break the contract role of the interface.
I think the throws keyword in Java is useful.
Throws (“checked exceptions”) is one of the worst design choices in Java.
looks good but we need somethings more
like multi inheritance,
like returned data type strongly in functions
….
First of all i want to say congrats! .NET Core 3 is a big step and your efforts for upgrading C# continuously are very appreciated!
But I am confused about “Default implementations of interface members”, yes , they would help with some design problems and workaround some limitations of the API`s but at the same time its breaking one of the most used and discussed OOP paradigms.
We have abstract classes, virtual members, overloading, overriding so on.
Interface as its name is a “CONTRACT” and it guides and obliges the implementors. It clarifies our intentions what we can and cannot. What we should and shouldn`t. It should stay simple. Simplicity is the way for clear and better design.
Consider the following metaphore:
Plain “Interface ” is the “PLUG” between electricity network and consumer devices. We know that there is nothing in between and we can standardize it and if electricity stops we know that there is a problem with the provider or switchboard. Its not our job to mess with it.
“Default implementations of interface members” – is a some kind of electrical scheme inside the plug. If something breaks we have extra layer of complexity and because it is easy accessible we can decide to check or fix it, and there is a problem. If we don`t have good skills we can harm ourselves or create another problem.
Yes, we are not obliged to use it, but there are million developers and we can`t rely on “may” or “maybe” … At least we should have some mechanism to switch it on off or another option in StyleCop.
Maybe I am wrong…. but i would like to see more arguments.
Async streams, indexers and ranges aren’t available in .Net 4.8. So this is the beginning of pushing devs to .Net Core?
Is support for generic attributes going to make it in C# 8?
I hope the non-nullable reference types will always be optional. I understand what you’re trying to do, but I tried this feature with Typescript and found it very cumbersome, with boilerplate checks all over the place. With Typescript it can be turned off (like most Typescript checks) and I hope this will always be the case with C# too. To be honest if it’s ever forced on me, I’ll probably fork Roslyn to turn it off, for my own use at least.
IAsyncEnumerable will be exposed in public interfaces, and so not having it will severely limit the amount of portable development that can take place. I understand most of the other features not making it into .NET 4.8, as they are features more targeted for use in implementation. But we need IAsyncEnumerable and async streams. Please make it happen!
I really hope that the features not needing runtime support will make it into .NET Standard 2.0 compatible Nuget packages, as has been done with features in the past like ValueTask
When we declare string s; C# automatically assign value null to it. Now in case of nullable reference types what will be the way forward for old code as well as new
People think string and string? Is a stupid design, this type of reference can be empty, have to add a nullable type of reference. Very contradictory
I’m really beginning to wonder if the language is beginning to become overly complex. Some of the new features appear rather idiosyncratic and must be shoe-horned into the (inflexible) C# grammar. For example the discussion of Index above doesn’t make sense to me (perhaps I need to re-read it) and the syntax in the recursive pattern example is terrible but I guess when you base a language on the primitive C grammar your options are limited. Perhaps it’s time to freeze C# and take everything that’s been learned and develop a fresh new language, perhaps using a better grammar (like say PL/I which has no reserved words and was thus infinitely extensible while always being backward compatible).
I wonder if this update will allow conditional assignment on properties when objects are not null…
outputLabel?.Text = “Hello”;
abilities it will bring.
The current plan is that C# 8.0 will ship at the same time as .NET Core 3.0. However, the features will start to come alive with the previews of Visual Studio 2019 that we are working on. As those come out and you can start trying them out in earnest, we will provide a whole lot more detail about the individual features. The aim of this post is to give you an overview of what to expect, and a heads-up on where to expect it.
New features in C# 8.0
Here’s an overview of the most significant features slated for C# 8.0. There are a number of smaller improvements in the works as well, which will trickle out over the coming months.
Nullable reference types
agario unblocked
I really cannot wait for the nullable reference types to become available. Will it be “visible” through reflection (i.e. will nullable reference types be marked with an attribute or something), so that analyzers can work on the assembly level also?