New Features in C# 7.3

综合技术 2018-05-18 阅读原文

Though a comparatively minor release, C# 7.3 addresses some long outstanding complaints from C# 1 and 2.

Overload Resolution

Since version 1.0 C#’s overload resolution rules had a rather questionable design. Under some circumstances it would pick two or more methods as candidates, though all but one couldn’t be used. Depending on the priority of the incorrectly selected method, the compiler would then either indicate no method matched or the matches were ambiguous.

C# 7.3 moves some of the checks to happen during overload resolution, rather than after, so the false matches don’t cause compiler errors. The Improved overload candidates
proposal outlines these checks:

1. When a method group contains both instance and static members, we discard the instance members if invoked without an instance receiver or context, and discard the static members if invoked with an instance receiver. When there is no receiver, we include only static members in a static context, otherwise both static and instance members. When the receiver is ambiguously an instance or type due to a color-color situation, we include both. A static context, where an implicit this instance receiver cannot be used, includes the body of members where no this is defined, such as static members, as well as places where this cannot be used, such as field initializers and constructor-initializers.
2. When a method group contains some generic methods whose type arguments do not satisfy their constraints, these members are removed from the candidate set.
3. For a method group conversion, candidate methods whose return type doesn't match up with the delegate's return type are removed from the set.

Generic Constraints: enum, delegate, and unmanaged

Developers have been complaining about the inability to indicate a generic type must be an enum since generics were introduced in C# 2.0. This has finally been addressed, and you can now use the enum
keyword as a generic constraint. Likewise, you can now use the keyword delegate
as a generic constraint

These don’t necessarily work the way you might expect. If the constraint is where T : enum
then someone can use Foo
when what you probably meant is for them to use a subclass of System.Enum
. Nonetheless, this should cover most scenarios for enums and delegates.

The Unmanaged type constraint
proposal uses the “unmanaged” keyword to indicate the generic type must be a “type which is not a reference type and doesn't contain reference type fields at any level of nesting.” This is intended to be used with low level interop code where you need to “create re-usable routines across all unmanaged types”. Unmanaged types include:

  • The primitives sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, IntPtr, or UIntPtr.
  • Any enum type.
  • Pointer types
  • User defined structs only containing the above.

Attributes on Hidden Fields

While auto-implemented properties are very useful, they do have some limitations You cannot apply attributes to the backing field, because you can’t see it. While not usually an issue, this can be problematic when dealing with serialization.

The Auto-Implemented Property Field-Targeted Attributes
proposal addresses this in a straight-forward fashion. When applying an attribute to an auto-implemented property, simply prepend the field:

public class Foo {

    [field: NonSerialized]
    public string MySecret { get; set; }

Tuple Comparisons (== and !=)

While the name of the proposal, Support for == and != on tuple types
, summarizes this feature nicely there are some details and edge cases to be aware of. The most important is a potentially breaking change,

If someone wrote their own ValueTuple types with an implementation of the comparison operator, it would have previously been picked up by overload resolution. But since the new tuple case comes before overload resolution, we would handle this case with tuple comparison instead of relying on the user-defined comparison.

Ideally this custom ValueTuple type would follow the same rules as the C# 7.3 compiler, but there may be subtle differences in how it handles nested tuples and dynamic types.

Expression variables in initializers

In a way this reads like an anti-feature. Rather than adding capabilities, Microsoft removed restrictions on where expression variables can be used.

We remove the restriction preventing the declaration of expression variables (out variable declarations and declaration patterns) in a ctor-initializer. Such a declared variable is in scope throughout the body of the constructor.
We remove the restriction preventing the declaration of expression variables (out variable declarations and declaration patterns) in a field or property initializer. Such a declared variable is in scope throughout the initializing expression.
We remove the restriction preventing the declaration of expression variables (out variable declarations and declaration patterns) in a query expression clause that is translated into the body of a lambda. Such a declared variable is in scope throughout that expression of the query clause.

The original reasoning for these restrictions were simply noted as “due to lack of time”. Presumably the restrictions reduced the amount of testing that needed to be done for previous versions of C# 7.

Stack Allocated Arrays

A rarely used, but important feature of C# is the ability to allocate an array inside the stack via the

. This may offer performance improvements over normal arrays, which are allocated on the heap and cause GC pressure.

int* block = stackalloc int[3] { 1, 2, 3 };

There is some danger in using a stack allocated array. Since it requires taking a pointer against the stack, it can only be used in an unsafe context. The CLR attempts to mitigate this by enabling buffer overrun detection, which will cause the application to “terminate as quickly as possible”.

With C# 7.3, you gain the ability to initialize the array when it is created just like you would with normal arrays. The proposal
lacks details, but Microsoft was considering pre-initializing a master array which then could be rapidly copied when the function is invoked. In theory this would be faster than creating an array and then initializing it element by element.

Note stack allocated arrays are meant for scenarios where you need lots of small, briefly used arrays. They shouldn’t be used for large arrays or deeply recursive functions, as you may exceed the amount of available stack space.

Stack Allocated Spans

A safe alternative to a stack allocated array is a stack allocated span. By eliminating the pointer, you also eliminate the possibility of a buffer overrun. Which in turn means you can use this without having to mark the method as unsafe.

Span block = stackalloc int[3] { 1, 2, 3 };

Note Span
requires the System.Memory
NuGet package.

Re-assignable Ref Locals

Ref local variables
can now be reassigned just like normal local variables.

For other C# 7.3 proposals
see the csharplang GitHub site.




C语言入门:输入任意一个正数(奇数),判断是否为质数... C语言永远不会过时 其实学编程关键是学习其思想,如果你精通了一门,再去学其他的时候也很容易上手。C不会过时的,尤其是在unix、linux操作平台上,学好C是必须的。 C跟C++在很多方面也是兼容的,c是c++的基础。 再者c能从很大的程度上帮你了解计算机的发展史,数据结构等方面...
安装 CMake 错误: c++: internal compiler error: Killed ... 根据 安装 CMake 时, 在运行完 ./bootstrap 的过程中出现这个错误: c++: internal compiler error: Killed (program cc1plus) Pleas...
dotnet new templates for serverless functions Since .NET Core was announced, I've really been loving the command line experience for .NET Core. Being able to use dotnet new to start a new applica...
Detouring code Microsoft Detours from Microsoft Research is a powerful technology to intercept operating system function calls and detour the call to your own code. ...
材质和纹理的开销是如何相互影响的?... 这是第105篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题, 建议阅读时间15分钟,认真读完必有收获。文末,我们的互动话题是 4.6是否值得尝鲜? UWA 问答社区 UWA QQ群:465082844(仅限技...