This is the demo code from the Code Conversations episode: C#7 Features with Mads Torgersen
C# 7.0 features script
using static System.Console;
classProgram
{
staticvoid Main(string[] args)
{
}
}
A Fibonacci number is the sum of the two previous Fibonacci numbers.
static int Fibonacci(int n)
{
}
An efficient implementation uses a recursive helper that returns the two latest numbers. We do that with tuples:
static(int, int) Fib(int i)
{
}
This is a tuple type, denoting two ints. For clarity we can give the elements descriptive names:
static (intcurr, intprev) Fib(int i)
We encode a base case with a tuple literal:
if (i == 0) return(1, 0);
Otherwise we need a recursive call:
var t = Fib(i - 1);
We can use the descriptive names to dot into the returned tuple, in order to create the values of our own return tuple:
return (t.curr + t.prev, t.curr);
Another approach is to deconstruct the tuple returned from the recursive call directly into new variables:
var (c, p) = Fib(i - 1);
return (c + p, c);
Full Fib method:
static (int curr, int prev) Fib(int i)
{
if (i == 0) return (1, 0);
var (c, p) = Fib(i - 1);
return (c + p, c);
}
We can now call the helper from the main Fibonacci method:
return Fib(n).curr;
But we can also deconstruct our way to the result, using a wildcard:
(var r, \_) = Fib(n);
return r;
Fib is a helper method, that is only useful within the Fibonacci method itself. It can be declared as a local function inside the body of the Fibonacci method:
static int Fibonacci(int n)
{
(var r, \_) = Fib(n);
return r;
(int curr, int prev) Fib(int i)
{
if (i == 0) return (1, 0);
var (c, p) = Fib(i - 1);
return (c + p, c);
}
}
Local variables from the enclosing method, such as n and r are in scope in the local function. Though we don't make use of that here, that is often useful.
Let's change it to a "TryFibonacci" method, that takes any object and tries to make an int out of it.
static bool TryFibonacci(object o, outint r)
{
(r, \_) = Fib(n);
return true;
Our job now is to see if we can get an n from the o that was passed in. Pattern matching can help with that:
if (o is int n)
{
(r, \_) = Fib(n);
returntrue;
}
This is a so-called type pattern. Apart from is-expressions it can also be used in cases of switch statements to switch on the type of an incoming object.
The pattern introduces a variable in the middle of an expression. Such a variable is called an expression variable, and is like any other local variable. For instance you can assign to it. Let's augment this to work on strings that parse to ints:
if (o isint n || o isstring s && int.TryParse(s, out n))
Adding fallback behavior, here is the full TryFibonacci method:
static bool TryFibonacci(object o, outint r)
{
if (o isint n || o isstring s && int.TryParse(s, out n))
{
(r, \_) = Fib(n);
return true;
}
r = 0;
return false;
(int curr, int prev) Fib(int i)
{
if (i == 0) return (1, 0);
var (c, p) = Fib(i - 1);
return (c + p, c);
}
}
Let's end by calling TryFibonacci from the Main method. As another form of expression variable, we can use freshly declared out variables directly as an out parameter:
static void Main(string[] args)
{
if (TryFibonacci("11", outint r)) WriteLine(r);
}
You can even declare out variables with "var", since the type can be inferred from the method signature:
if (TryFibonacci("11", outvar r)) WriteLine(r);
using static System.Console;
class Program
{
static bool TryFibonacci(object o, out int r)
{
if (o is int n || o is string s && int.TryParse(s, out n))
{
(r, _) = Fib(n);
return true;
}
r = 0;
return false;
(int curr, int prev) Fib(int i)
{
if (i == 0) return (1, 0);
var (c, p) = Fib(i - 1);
return (curr: c + p, prev: c);
}
}
static void Main(string[] args)
{
if (TryFibonacci("11", out var r)) WriteLine(r);
}
}