Update: In C#7 you can easily assign multiple variables at once using tuples. In order to assign array elements to variables, you'd need to write an appropriate Deconstruct() extension methods:
Another way to consume tuples is to deconstruct them. A deconstructing
  declaration is a syntax for splitting a tuple (or other value) into
  its parts and assigning those parts individually to fresh variables:
(string first, string middle, string last) = LookupName(id1); // deconstructing declaration
WriteLine($"found {first} {last}.");
In a deconstructing declaration you can use var for the individual
  variables declared:
(var first, var middle, var last) = LookupName(id1); // var inside
Or even put a single var outside of the parentheses as an
  abbreviation:
var (first, middle, last) = LookupName(id1); // var outside
You can also deconstruct into existing variables with a deconstructing
  assignment:
(first, middle, last) = LookupName(id2); // deconstructing assignment
Deconstruction is not just for tuples. Any type can be deconstructed,
  as long as it has an (instance or extension) deconstructor method of
  the form:
public void Deconstruct(out T1 x1, ..., out Tn xn) { ... }
The out parameters constitute the values that result from the
  deconstruction.
(Why does it use out parameters instead of returning a tuple? That is
  so that you can have multiple overloads for different numbers of
  values).
class Point
{
    public int X { get; }
    public int Y { get; }
    public Point(int x, int y) { X = x; Y = y; }
    public void Deconstruct(out int x, out int y) { x = X; y = Y; }
}
(var myX, var myY) = GetPoint(); // calls Deconstruct(out myX, out myY);
It will be a common pattern to have constructors and deconstructors be
  “symmetric” in this way.
  https://blogs.msdn.microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/
Old answer:
In fact, you can achieve similar functionality in C# by using extension methods like this (note: I haven't include checking if arguments are valid):
public static void Match<T>(this IList<T> collection, Action<T,T> block)
{
    block(collection[0], collection[1]);
}
public static void Match<T>(this IList<T> collection, Action<T,T,T> block)
{
    block(collection[0], collection[1], collection[2]);
}
//...
And you can use them like this:
new[] { "hey", "now" }.Match((str1, str2) =>
{
    Console.WriteLine(str1);
    Console.WriteLine(str2);
});
In case a return value from a function is needed, the following overload would work:
public static R Match<T,R>(this IList<T> collection, Func<T, T, R> block)
{
    return block(collection[0], collection[1]);
}
private string NewMethod1()
{   
    return new[] { "hey", "now" }.Match((str1, str2) =>
    {
        return str1 + str2;
    });
}
In this way:
- You avoid having to repeat array name like in solution proposed by JaredPar and others; the list of "variables" is easy to read. 
- You avoid having to explicitly declare variables types like in Daniel Earwicker's solution. 
The disadvantage is that you end up with additional code block, but I think it's worth it. You can use code snippets in order to avoid typing braces etc. manually.
I know it's a 7 years old question, but not so long time ago I needed such a solution - easy giving names to array elements passed into the method (no, using classes/structs instead of arrays wasn't practical, because for same arrays I could need different element names in different methods) and unfortunately I ended up with code like this:
var A = points[0];
var A2 = points[1];
var B = points[2];
var C2 = points[3];
var C = points[4];
Now I could write (in fact, I've refactored one of those methods right now!):
points.Match((A, A2, B, C2, C) => {...});
My solution is similar to pattern matching in F# and I was inspired by this answer: https://stackoverflow.com/a/2321922/6659843