Generating unique names for things like filenames
Potential infinite loop
If the isValid method is faulty, i.e always returns false, we'd have an infinite loop on our hands. How do we fix it? I'd fix it in coding in some (configurable but not necessary) upper limit, say 255, so if we tried 255 times, and it still fails, just throw an exception. This could either be done using a public property with a default value, or an optional constructor parameter, then all you'd need to do is check whether i is less than that upper bound.

Naming, specifically underscores
Technically, there's nothing wrong with your naming right now, as the Microsoft Naming Guidelines is outdated (from 2008) and not complete at all. And this Stack Overflow Q&amp;A states that you shouldn't use underscores, and instead use this to differentiate between members and parameters (as you are right now). But even that is from 2012, and looking at some of Microsoft's own code, like this line in the ASP.NET Core repository shows that even they use an underscore prefix.


Resulting code:
public class NameGenerator
{
    private readonly string _baseName;

    private readonly Func&lt;string, bool&gt; _isValid;

    public int MaxAttempts {get; set;} = 255;

    public NameGenerator(string baseName, Func&lt;string, bool&gt; isValid)
    {
        _baseName = baseName;
        _isValid = isValid;
    }

    public string Generate()
    {
        // This is just a personal preference, keep it as one line if you want
        if (_isValid(_baseName))
            return _baseName;

        for (var i = 0; i &lt; MaxAttempts; i++)
        {
            var candidate = $&quot;{_baseName} ({i})&quot;;
            if (_isValid(candidate))
                return candidate;
        }
    }
}

Instead of a property to set the max attempts, you could also use an optional constructor parameter, like this:
public class NameGenerator
{
    // ...
    private readonly int _maxAttempts;

    public NameGenerator(string baseName, Func&lt;string, bool&gt; isValid, int maxAttempts = 255)
    {
        // ...
        _maxAttempts = maxAttempts;
    }

    // ...
}