Making the chars in the array nullable and then later replacing the null chars by '-' seems over complicated. Instead, insert them directly when required. The loop variable is the very array index we need and there is no point in counting characters.
const string PasswordCharacters = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";
const int ChunkLength = 4, NumberOfChunks = 4;
const int TotalPassordLength = ChunkLength * NumberOfChunks + NumberOfChunks - 1;
char[] pwArray = new char[TotalPassordLength];
Random r = new Random();
for (int i = 0; i < pwArray.Length; i++) {
if ((i + 1) % (ChunkLength + 1) == 0) {
pwArray[i] = '-';
} else {
pwArray[i] = PasswordCharacters[r.Next(PasswordCharacters.Length)];
}
}
string pw = new String(pwArray);
Console.WriteLine(pw);
Console.ReadKey();
prints e.g.:
iDAu-Fcqi-CBdB-zyRp
It is easier to delegate all the calculations to our code by declaring some constants. This also makes it very easy to change these parameters in the future. Note that expressions involving only constants are calculated at compile time. Therefore they will not have a negative impact at runtime. And even if they would - we are talking about nanoseconds. So, prefer a robust code over unnecessary optimization.
The modulo operator % returns the remainder of an integer division. Since we do not want to insert a '-' at position 0, I calculate (i + 1) % 5, so that the first index with an i + 1 divisible by 5 (i.e., with a remainder of 0) will be 4 and then 9 and 14.
Using pwArray.Length in the loop condition instead of the hard-coded 19 has the advantage that the condition will automatically adapt to any array length changes. But since I introduced a constant for the password length, we could also use this constant instead.
System.String (with the C# alias string) has a constructor accepting a char array. This is more performing than String.Join.
I renamed letters to PasswordCharacters and made it a constant. The new name allows you to add other characters like digits without having to change the name. Btw., the order of the characters in this string does not matter, as we pick them randomly anyway. So, you could have written them in alphabetical order without making the resulting password less random.
pwArray.Count(c => c != null)<- this is too shocking, do you know that you can just set hyphen like this:pwArray[iter + 1] = '-'?