When setting a binding on a Setter you don't need BindingOperations at all. All you need to do is:
var setter = new Setter(TextBlock.TextProperty, new Binding("FirstName"));
or equivalently
var setter = new Setter
{
Property = TextBlock.TextProperty,
Value = new Binding("FirstName"),
};
either of these would be equivalent to
<Setter Property="TextBlock.Text" Value="{Binding FirstName}" />
The reason this works is that Setter.Value is an ordinary CLR property, not a DependencyProperty and as such it can't be bound. So there is no ambiguity in either the XAML or the code when you store a Binding object in it.
When the Setter is actually applied to an object, if a Binding is found in the Setter, the equivalent of BindingOperations.SetBinding is called. Otherwise the property is set directly.