The idea of defensive programming is to handle common errors in advance, and, it is based on the disturbing :) fact that there will be bugs ... yours, and the ones your end-users find with their unexpected behaviors. Better you should find them !
.NET/C# provide several facilities that handle common errors in accessing common data structures, parsing strings into numeric types, etc. These are expressed with function semantics like 'TryGetValue, 'TryParse, which perform some operation, and return a boolean indicating the operation's success or failure.
So, let's write a function with similar semantics to handle getting a substring:
using System;
namespace YourNameSpace
{
public static class StringExtensions
{
public static bool TryGetSubString(this string str, out string substring, uint start = 0, uint howmany = 0, bool dothrowerrors = false)
{
bool result = false;
substring = null;
if (string.IsNullOrEmpty(str))
{
if (dothrowerrors)
{
throw new ArgumentException($"'{nameof(str)}' cannot be null or empty", nameof(str));
}
else
{
return result;
}
}
int len = str.Length - 1;
if (howmany == 0 || start > len || start + howmany > len)
{
if (dothrowerrors)
{
throw new ArgumentException($"'{nameof(str)}' is not long enough for these indexes", nameof(str));
}
else
{
return result;
}
}
substring = str.Substring((int) start, (int) howmany);
return true;
}
}
}
Notes:
1) by using type 'uint for the index/length parameters we prevent entering a negative int.
2) we are using the Extension method technique offered by C# >= 3.0 here
Use example:
private string test = "0123456";
if (test.TryGetSubString(out string sresult, 5,1))
{
}
else
{
}