I know this one is pretty old, but I encountered an issue recently with having to do multiple replacements to make a file name safe.  First, in the latest .NET string.Replace function null is the equivalent to empty character.  Having said that, what is missing from .Net is a simple replace all that will replace any character in an array with the desired character.  Please feel free to reference the code below (runs in LinqPad for testing).
// LinqPad .ReplaceAll and SafeFileName
void Main()
{
    ("a:B:C").Replace(":", "_").Dump();                     // can only replace 1 character for one character => a_B_C
    ("a:B:C").Replace(":", null).Dump();                    // null replaces with empty => aBC
    ("a:B*C").Replace(":", null).Replace("*",null).Dump();  // Have to chain for multiples 
    // Need a ReplaceAll, so I don't have to chain calls
    ("abc/123.txt").SafeFileName().Dump();
    ("abc/1/2/3.txt").SafeFileName().Dump();
    ("a:bc/1/2/3.txt").SafeFileName().Dump();
    ("a:bc/1/2/3.txt").SafeFileName('_').Dump();
    //("abc/123").SafeFileName(':').Dump(); // Throws exception as expected
}
static class StringExtensions
{
    public static string SafeFileName(this string value, char? replacement = null)
    {
        return value.ReplaceAll(replacement, ':','*','?','"','<','>', '|', '/', '\\');
    }
    public static string ReplaceAll(this string value, char? replacement, params char[] charsToGo){
        if(replacement.HasValue == false){
                return string.Join("", value.AsEnumerable().Where(x => charsToGo.Contains(x) == false));
        }
        else{
            if(charsToGo.Contains(replacement.Value)){
                throw new ArgumentException(string.Format("Replacement '{0}' is invalid.  ", replacement), "replacement");
            }
            return string.Join("", value.AsEnumerable().Select(x => charsToGo.Contains(x) == true ? replacement : x));
        }
    }
}