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的话,还不如使用随机森 ...
随机推荐
- cplusplus 库 在线管理; 类似于 python的 pip install 、nodejs 的npm模块
cplusplus 库 在线管理: 类似于 python的 pip install .nodejs 的npm模块 还有 apache 经常使用的 Apache Ivy 项目依赖管理工具/Maven 这 ...
- 中文分词库及NLP介绍,jieba,gensim的一些介绍
六款中文分词软件介绍: https://blog.csdn.net/u010883226/article/details/80731583 里面有jieba, pyltp什么的.另外下面这个博客有不少 ...
- 条件随机场(CRF)理论及应用
http://x-algo.cn/index.php/2016/02/15/conditional-random-field-crf-theory-and-implementation/ 条件随机场( ...
- ASP.NET压力测试
本文导读:对于直接面对互联网用户的WEB应用,在开发设计的时候必须格外小心,因为谁也不知道在单位时间内WEB程序访问和运行的速度.所以,在程序设计完成以后,最后针对程序进行一些严格的甚至是苛刻的测试, ...
- protobuf-c的学习总结
1.前言 项目中用到protobuf-c进行数据序列化,好处在于后期程序扩展性非常好,只需要改动proto的定义就可以保持兼容,非常的灵活方便.关于protobuf-c的详细介绍可以参考google官 ...
- 腾讯下载的视频转换为MP4
第一步:首先找到腾讯视频下载设置中的缓存目录,如下图 打开这个目录,找到最近的,就是刚才你下载的文件夹 打开最近的文件夹,如下图,copy里面的内容到D盘的qlv目录中 第二部:进入D盘的qlv目录, ...
- 在linux 中wget 无法解析主机
vim /etc/resolv.cof 在里面加入节点 nameserver 8.8.8.8 / nameserver 8.8.4.4 即可 失败时: 成功时:
- Java集合遍历时删除
public static void main(String[] args){ List<Integer> list = new ArrayList<Integer>(); l ...
- [PureScript] Introduce to PureScript Specify Function Arguments
JavaScript does its error-checking at runtime, but PureScript has a compiler, which makes sure that ...
- ASP入门(一)环境的搭建
突然转战ASP是因为,手头要实现一个类似管理系统的东东,正好把ASP再从头学习一下下. ASP可以做什么? ASP,它的原文是 Active Server Pages . ASP最核心的扩展内容:Ac ...