C# 7 out variables, tuples & other new features
C# 7 out variables, tuples & other new features
C# 7 is available on new Visual Studio 2017 and comes with some new features. I wouldn’t call them ‘revolutionary’ features, but they add on the language very well and some of them can be very helpful. Personally, I have anticipated some features since C# 6 version.
TLDR; Sample code can be found here.
C# 7 comes with new features that I really like and I strongly believe they will be very useful in daily coding life, while others I think won’t help me anytime soon.
Look at C# Language Design on GitHub or the proposals made from the community for the language, to get an idea of the new language features and the features to come.
Before I begin, I wish to share my experience with VS 2017, C# 7, .NET Standard/Core.
I used Visual Studio 2017 Community version to experiment with new syntax (I don’t have yet a license for Pro, sniff). I also wanted to experiment on code by writing some unit tests (this is my way to learn new stuff, I first write a unit test and then fiddle around with code, aiming to make it pass), so I used xUnit (which I am not a big fan of, but anyways). Both projects were .NET Standard 1.4 projects, but then I realized that this doesn’t work, as you can see on my SOpost. So, the answer was to make the test project a runnable application, I used .NET Core 1.1 but with no luck, so what I did in the end, was to create a new test project, targeting .NET Framework 4.6.2. Project with samples remained to .NET Standard.
Also, do not forget to use the System.Runtime Nuget package. Take a look on samples at Github (link is available above).
Out variables
Man, I love this. It is the first topic to discuss because I anticipated this for a long time. Personally, I really, really hated the old way, where you needed to declare a variable before calling the method with the output parameter. I was feeling funny everytime I was forced to code stuff like this:
int value; int .TryParse( "5" , out value); // Use value |
I strongly believe that this is not elegant and I was really disappointed when they decided to not include this on C# 6 release.
But, here it is. Now you can declare the variable and the out parameter on the same line
int .TryParse( "5" , out int value); // Use value |
Yeah, I know, this isn’t exactly a revolution or an outstanding feature, but it is helpful and increases readability (of course, you need to get your eyes used to this new syntax, as you might lose a variable declaration while reading). Please note also that you can declare it as a var,
instead of a specific type.
As it is expected, you can declare more than one output parameters (duh!) and use this new syntax. The cool thing with this though, is that you can decide which output parameter you wish and which you don’t. Cool stuff!
Bear with me, I know, the following example is a bit dull, but it demonstrates the use of discards (which can be declared with an underscore)
// Usage with two output parameters, fetching the first CustomConverter.GetIntegerFromStringWithDiscards( out var x, out _); // Usage with more than two output parameters, fetching the first CustomConverter.GetIntegerFromStringWithDiscards( out var x, out _, out _); // Usage with three output parameters, fetching the second CustomConverter.GetIntegerFromStringWithDiscards( out _, out var y, out _); |
As you can see, you omit the parameter you don’t wish to return using out _. How cool is the third example? You discard the first and the last and you get the middle one. Nice!
There is one gotcha though and this has to do with scoping. The scope rule is strict, having the variables accessible only in the scope body they are declared in.
That said, the following code will not work, you will get a compiler error, stating that the name ‘[name]’ does not belong to current context.
public static void GetIntegerFromStringWithDiscards( out int x, out int y) { if ( true ) { int .TryParse( "5" , out var a); int .TryParse( "6" , out var b); } x = a; y = b; } |
but the following will work just fine
public static int GetIntTimesTenOrDefault( string number) { if ( int .TryParse(number, out int value)) { return value * 10; } // Value variable is visible to this scope, not only inside the if scope return value; } |
Tuples and deconstruction
The new syntax of tuples is awesome! I really like it and it kinda reminds me of JavaScript objects in some weird way, probably it’s just me, of course the syntax is not the same, but it reminds me that whenever I want to fetch a tuple value with that syntax.
Please note, in order to use the new tuples, you need to download a Nuget package, as it is not yet on core language code. So, that said, download the
System.ValueTuple 4.3.0
(or higher)
Forget the old, rusty, rigid syntax for tuples, now you just declare them by type and you have them.
Look on the following example, the GetPerson
method returns a Tuple with two strings and an integer.
public ( string name, string surname, int age) GetPerson() { return ( "George" , "Dyrrachitis" , 28); } |
The syntax is pretty much simple. For declaration, you wrap in parenthesis the tuple items with their types and optionally names for them, if you want to make the signature more readable.
In order to create a tuple instance, you wrap your objects in parenthesis, of course matching the declaration, like in the example above, first define the two strings, then the integer value.
You consume it in a similar fashion. Of course, you can consume a simple tuple object and access each item individually like this:
var result = person.GetPerson(); // result.Item1 // result.Item2 // result.Item3 |
But this is not sexy, okay? What about this?
// Act ( string firstName, string lastName, int age) = person.GetPerson(); // Assert Assert.Equal( "George" , firstName); Assert.Equal( "Dyrrachitis" , lastName); Assert.Equal(28, age); |
That’s what I’m talking about! You can wrap your tuple elements in parenthesis, declare them and they are ready to be used like normal variables. Of course, you can declare them as var
as well or even better, for shorthand syntax (see deconstruction below):
var (firstName, lastName, age) = person.GetPerson(); |
Declaring them all as vars.
Also, like out variables, tuples have discards as well, so you can omit elements you don’t wish to use, just like at the following example.
[Fact] public void GetOnlyFirstNameGeorgeFromTuple() { // Arrange var person = new Person(); // Act ( var firstName, _, _) = person.GetPerson(); // Assert Assert.Equal( "George" , firstName); } [Fact] public void GetFirstNameAndAgeFromTuple() { // Arrange var person = new Person(); // Act ( var firstName, _, var age) = person.GetPerson(); // Assert Assert.Equal( "George" , firstName); Assert.Equal(28, age); } |
Finally, on tuples, there is another interesting feature, called deconstruction. We looked at that feature on the previous examples, with the sexy syntax.
But you can use it on classes as well, making them to behave like a tuple. You just need to declare a method called Deconstruct
in your class, which can receive a number of output parameters, which will be deconstructed.
public class Person { public Person( string firstName, string lastName, int age) { FirstName = firstName; LastName = lastName; Age = age; } public string FirstName { get ; } public string LastName { get ; } public int Age { get ; } public void Deconstruct( out string firstName, out string lastName, out int age) { firstName = FirstName; lastName = LastName; age = Age; } } |
In the code above, I want to deconstruct a Person
object to strings firstName,
lastName
and integer age
. So, I use the Deconstruct
method, declaring three output parameters, matching the description I gave earlier. I just give them the values from the public properties of the class, which were populated on instantiation.
Now, Person
can be deconstructed like this:
[Fact] public void DeconstructPersonObjectWithDeconstructor() { // Arrange var person = new Person( "George" , "Dyrrachitis" , 28); // Act var (firstName, lastName, age) = person; // Assert Assert.Equal( "George" , firstName); Assert.Equal( "Dyrrachitis" , lastName); Assert.Equal(28, age); } |
Groovy!
Pattern matching
Although pattern matching is a hot feature for the language, I won’t spend much time discussing that.
I would say that pattern matching is not that really useful to the language, it was a feature ‘nice to have’ mostly, by reading the patterns.md proposal, at least that is my take.
You can think of it as the Is
operator in steroids. That operator is extended to test an expression against a pattern, and in this case we have various types of patterns.
We can match an expression based on type or constant value or null and other discussed here.
This feature is very useful in some coding scenarios. Mostly, I like it because it gives me better syntax in an is-a situation or in testing a nullable type. Seriously, I like it only for that.
Check on the following example (of course the following can shorten significantly by using a ternary operator, but for the sake of the example I am more explicit)
public class TypesWithIsOperator { public bool IsCustomReferenceType( object obj) { if (obj is CustomReferenceType t) { return t.True; } return false ; // or for short: return obj is CustomReferenceType t ? t.True : false; } public bool IsNullableIntGreaterThanTen( int ? value) { if (value is int v) { return v > 10; } return false ; // or for short: return value is int v ? v > 10 : false; } } public class CustomReferenceType { public bool True => true ; } |
In Is
CustomReferenceType
method, I check if the object passed is of type CustomReferenceType
and I create a new variable t
of that type from the object (same as using the as
operator and assigning the result to a new variable). Same goes for the IsNullableIntGreaterThanTen
method, in which I check if the nullable integer value passed is not null, create a new variable v
, assing the integer value there and return Boolean result based on its value.
In essence, I avoid doing this:
// With reference type var t = obj as CustomReferenceType; if (t != null ) { return t.True; } return false ; // Or with value type if (value.HasValue) { int v = value.Value; return v > 10; } return false ; |
Another coding scenario that pattern matching is useful and is demonstrated by almost everyone, is when using a switch-case statement. Now, you can match a case block by a pattern, which can be a type or a constant expression or null.
Be careful though, the patterns are not matched in an ordered fashion, as the compiler can match patterns out of order, as it is optimized to reuse the results of an already matched pattern in order to compute the result of matching of other patterns.
Also, note that the default
case will execute last, regardless of the order you specify it on code.
public string MatchingMachineProduct(IMachine machine) { switch (machine) { case PizzaMachine pizzaMachine: return pizzaMachine.Make(); case FishAndChipsMachine fishAndChipsMachine: return fishAndChipsMachine.Make(); default : return null ; case null : throw new ArgumentNullException(nameof(machine)); } } |
Other features
Local functions
Other features that I like are the local functions. I think this is a good addition and improves readability.
When reading on code, as a human being, I read from top to bottom and jumping to references while reading, meaning I will jump to the next function declared. I prefer to write a private function immediately after its call, so a reader can jump to it directly, without much effort searching in the class.
With local functions, you literally have the chance to write it just after your call in the caller’s body. All the caller’s scoped variables are available within the local function, which helps you to define more readable signatures, with less parameters (the more the parameters a method has, the dirtier it is).
Of course, this is one advantage that it gives you, the other is scoping, the only one that has access to this method is the caller and only that. This can help you create more meaningful inner functions, closer to the caller’s context.
public class Bubblesort { private readonly int [] _array; public Bubblesort( int [] array) => _array = array; public void Sort() { for ( var i = _array.Length - 1; i > 1; i--) for ( var j = 0; j < i; j++) if (_array[j] > _array[j + 1]) Swap(j, j + 1); void Swap( int i, int j) { var temp = _array[i]; _array[i] = _array[j]; _array[j] = temp; } } } |
Be careful though, not to overdo it, as this can lead to some insanely huge looking parent methods, which is the worst thing you can do with your code. As a rule of thumb, use local functions only when you know they will be short, else use other private methods and try to separate concerns, do not make methods more 10-20 lines long.
Always remember:
- 1-10 lines is perfect
- 10-20, you need a short check on code
- 20-30, it’s getting out of hand
- 50-60+, Houston, we have a problem
If your method does not fit your screen, then you are in some deep trouble. And don’t cheat with pivot screens.
Expression bodied members
Finally, this is another great feature. I loved expression bodied members in the previous release, my single line methods looked so good with that syntax.
With the new syntax, constructors, destructors, even getters and setters can have expression bodies.
Look on the previous example, the constructor has an expression body.
Throw expressions
You can throw an exception when using the ternary conditional operator (?:
), the null coalescing operator (??
) or as the body of an expression-bodied lamda or method.
Take a look at the following examples:
// Using the ternary conditional operator return condition ? "Yes, it's true" : throw new Exception( "Oops" ); // Using the null coalescing operator return value ?? throw new Exception( "Oops" ); // Expression-bodied method public void Throws() => new Exception( "Oops" ); |
Summary
Cool new features, improving your experience with the language.
I haven’t been through many other features, maybe the most notorious of them, the pattern matching. This is on purpose. As I said in the beginning, my intention was to go through stuff that I find useful in my daily routine, and for the moment, I do not find pattern matching that useful for me. Probably I need to dig deeper and see the advantages, but for the moment I do not use it.
What new features do you use? Do they come in handy for you?
C# 7 out variables, tuples & other new features的更多相关文章
- Machine Learning : Pre-processing features
from:http://analyticsbot.ml/2016/10/machine-learning-pre-processing-features/ Machine Learning : Pre ...
- Halcon相关
1.Halcon的自我描述 Program Logic Ø Each program consists of a sequence of HALCON operators Ø The progra ...
- .NET和C#的版本历史
维基百科页面:https://en.wikipedia.org/wiki/.NET_Framework_version_history Versionnumber CLRversion Release ...
- modern-cpp-features
C++17/14/11 Overview Many of these descriptions and examples come from various resources (see Acknow ...
- SSAS 通过 ETL 自动建立分区
一.动态分区的好处就不说了,随着时间的推移,不可能一个度量值组都放在一个分区中,处理速度非常慢,如何动态添加分区,如何动态处理分区,成为了很多新手BI工程师一个头痛的问题,废话不多说,分享一下我的经验 ...
- SSAS动态添加分区(一)
一.动态分区的好处就不说了,随着时间的推移,不可能一个度量值组都放在一个分区中,处理速度非常慢,如何动态添加分区,如何动态处理分区,成为了很多新手BI工程师一个头痛的问题,废话不多说,分享一下我的经验 ...
- SSAS动态添加分区 (转载)
一.动态分区的好处就不说了,随着时间的推移,不可能一个度量值组都放在一个分区中,处理速度非常慢,如何动态添加分区,如何动态处理分区,成为了很多新手BI工程师一个头痛的问题,废话不多说,分享一下我的经验 ...
- Automake
Automake是用来根据Makefile.am生成Makefile.in的工具 标准Makefile目标 'make all' Build programs, libraries, document ...
- 时间序列预测——深度好文,ARIMA是最难用的(数据预处理过程不适合工业应用),线性回归模型简单适用,预测趋势很不错,xgboost的话,不太适合趋势预测,如果数据平稳也可以使用。
补充:https://bmcbioinformatics.biomedcentral.com/articles/10.1186/1471-2105-15-276 如果用arima的话,还不如使用随机森 ...
随机推荐
- 2、Python特征
Python特征 Python编程语言中的定位 脚本语言 高阶动态编程语言 简单易学 Python是一种代表简单主义思想的语言.Python的这种伪代码本质是它最大的优点之一.它使你能够专注于解决问题 ...
- fatal error LNK1104: 无法打开文件“libc.lib”的问题 (转)
今天,编译程序的时候,意外遇到了一个错误,就是VS2008一直提示:fatal error LNK1104: 无法打开文件“libc.lib”,后来在网上查找了很多资料,终于知道原因了... 如果将用 ...
- Ubuntu环境变量解析
在Ubuntu中有如下几个文件可以设置环境变量 /etc/profile:在登录时,操作系统定制用户环境时使用的第一个文件,此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行. /e ...
- 【Spark】SparkStreaming-高可用-HA-Master
SparkStreaming-高可用-HA-Master sparkstreaming master 高可用_百度搜索 Spark:Master High Availability(HA)高可用配置的 ...
- Using Timers in MFC Applications
Timer Events in MFC Applications Event timers are always handy to have around and useful in nearly e ...
- OpenGL ES 3.0 and libGLESv2
note that libGLESv2 is the recommended Khronos naming convention for the OpenGL ES 3.0 library. This ...
- 有用的java工具
1.Jsoup html页面解析 2.FastJson java中json处理工具,类似于gson 3.jodd 类似于apache commons的一些常用工具集 4.Selenium IDE we ...
- oauth2-server-php-docs 概念
PHP的OAuth2服务器库 将OAuth2.0干净地安装到您的PHP应用程序中. 从GitHub 下载代码开始. 要求 这个库需要PHP 5.3.9+.然而,有一个稳定的版本和开发分支的PHP 5. ...
- Android 设计原则【转载+整理】
原文地址 本文内容 吸引我的眼球 简化我的生活 让我眼前一亮 在使用过大量 Android APP 后,你会发现,遵循了下面这些原则的 APP 将会有更好的用户体验. 我们知道,往往国企的那些软件,都 ...
- 入门GTD时间管理系统必读
让我们从什么时间管理开始.什么是时间管理呢?嗯,时间管理就是管理时间.可是,时间怎么能够管理呢? 其实我们管理地并不是时间,而是我们做的事.我们将事情分配到合适的时间段中,在有限的精力中完成它们,得到 ...