Check if a string contains matching brackets Trying to solve a case for a set of multiple types of brackets.

class BracketHelper {
    // checks if the string contains properly formatted brackets
    public static bool ProperBrackets(string s) {
        int p = 0;
        return ProperBrackets(s.ToCharArray(), ref p);
    }

    // main method, uses recursion to check if the brackets are properly formatted
    private static bool ProperBrackets(char[] arr, ref int ptr, char oppBracket = ' ') {

        for(;ptr&lt;arr.Length;ptr++) {
            var ch = arr[ptr];

            if ( IsOpen(ch)) {  // found an open bracket - let's check the inner content
                ptr +=1; // start checking from the next position
                if (ProperBrackets(arr, ref ptr, OppositeOf(ch)) == false) // inner content is malformed?
                    return false;
            }
            if ( IsClose(ch) ) // found a closing bracket
                return ch == oppBracket; // is this what we were looking for? If not - then we found a malformity!
        }

        // we reached the end. are we still searching for the closing bracket?
        return oppBracket == ' ';
    }

    private static char[] OpenBrackets = new char[] { '{', '[', '(' };
    private static char[] CloseBrackets = new char[] { '}', ']', ')' };

    // check helpers
    private static bool IsOpen(char ch)  {      return OpenBrackets.Contains(ch);       }
    private static bool IsClose(char ch) {      return CloseBrackets.Contains(ch);      }

    // returns a closing bracket character
    private static char OppositeOf(char ch) {
        for(var i=0;i&lt;OpenBrackets.Length;i+=1)
            if ( ch == OpenBrackets[i] ) 
                return CloseBrackets[i];
        throw new Exception($"'{ch}' is not an open bracket");
    }
}


Some test cases for LinqPad:

(BracketHelper.ProperBrackets("{}") == true).Dump();
(BracketHelper.ProperBrackets("{[]()}") == true).Dump();
(BracketHelper.ProperBrackets("{[()][{}]{}}") == true).Dump();
(BracketHelper.ProperBrackets("{[}]}") == false).Dump();
(BracketHelper.ProperBrackets("{[{(}])}") == false).Dump();
(BracketHelper.ProperBrackets("{[][][][{(})}]}") == false).Dump();
(BracketHelper.ProperBrackets("{[]}}") == false).Dump();
(BracketHelper.ProperBrackets("{") == false).Dump();


This can be done with the ref argument, so the pointer is a member of a class but in this case the method won't be static and I just wanted to make its usage as convenient as possible.
Let's first take a look at the code 


The code could use some space to breathe by adding spaces before and after operators. For example for(;ptr&lt;arr.Length;ptr++) would be more readable like
for (; ptr &lt; arr.Length; ptr++)  
The expected bracing style would be to place an opening bracket on a new line.  
Omitting braces {} although they might be optional can lead to hidden and therefor hard to find bugs. I would like to encourage you to always use them.  
Comments should describe why something is done. Comments like if ( IsClose(ch) ) // found a closing bracket are superflous and reduce the readability of the code. 
I know that naming things is a hard task but one should at least try to do this in a way that a reader of the code can grasp the meaning of a variable fast which isn'tthe case for oppBracket.  If a method returns a bool one should name that method like a question. E.g AreBracketsProper would be a better name.




Using recursion is a way one can use, but recursion isn't the holy grail for such a task. You need a ref int and an optional oppBracket to achieve the goal by using recursion.  

I would use a Stack&lt;Stack&lt;T&gt;&gt; for keeping track of the nesting levels and instead of two char[] arrays I would use two strings and would use IndexOf(char) to get the index of a bracket and to check if the current char is a bracket at all.  

If I find an opening bracket I will create a new Stack&lt;int&gt; which contains the value of openingBrackets.IndexOf() and then I will Push() it on the Stack&lt;Stack&lt;int&gt;&gt;. If I find an closing bracket I will take the last index from the Stack&lt;int&gt; if ther is any and check if it is the same as the value returned from closingBrackets.IndexOf(). This coul truned into an extension method as well, like so 

static class BracketHelper
{
    private const string openingBrackets = "{[(";
    private const string closingBrackets = "}])";

    public static bool AreBracketsProper(this string value)
    {
        if (value == null) { throw new ArgumentNullException(nameof(value)); }
        if (string.IsNullOrWhiteSpace(value)) { return true; }

        var stacks = new Stack&lt;Stack&lt;int&gt;&gt;();
        var currentStack = new Stack&lt;int&gt;();
        foreach (char c in value)
        {
            var bracketIndex = 0;
            if ((bracketIndex = openingBrackets.IndexOf(c)) &gt; -1)
            {
                currentStack = new Stack&lt;int&gt;();
                currentStack.Push(bracketIndex);
                stacks.Push(currentStack);
            }
            else if ((bracketIndex = closingBrackets.IndexOf(c)) &gt; -1)
            {
                while (currentStack.Count == 0 &amp;&amp; stacks.Count &gt; 0)
                {
                    currentStack = stacks.Pop();
                }

                if (currentStack.Count == 0 || bracketIndex != currentStack.Pop()) { return false; }
            }
        }
        return stacks.Count == 0 || stacks.Pop().Count == 0;
    }
}


Which could be called as an extension method like so   

("{}".AreBracketsProper() == true).Dump();
("{[]()}".AreBracketsProper() == true).Dump();
("{[()][{}]{}}".AreBracketsProper() == true).Dump();
("{[}]}".AreBracketsProper() == false).Dump();
("{[{(}])}".AreBracketsProper() == false).Dump();
("{[][][][{(})}]}".AreBracketsProper() == false).Dump();
("{[]}}".AreBracketsProper() == false).Dump();
("{".AreBracketsProper() == false).Dump();
("}".AreBracketsProper() == false).Dump();