- "I'm glad you had a stomachache here so I don't have to"
Today, we spent the full meeting digging into the pros and cons of conditional evaluation of interpolation holes, to ensure that we feel
comfortable with the changes doing so will bring. The real challenge here is that conditional evaluation of interpolation holes will break
with current mental models of interpolated strings: today, interpolated string holes are unconditionally evaluated in lexical order, and
the way the proposal is designed makes it possible for a library update to change whether the expressions in an interpolation hole is
evaluated or not. While library updates can always bring changes in behavior, this is the type of change that is currently only possible
by a library adding the Conditional
attribute onto a method or changing the type of an exception being thrown, causing a catch handler
to no longer be hit. Adding a new instance method that is preferred over an extension method is similar, but except in rare cases involving
user-defined conversions this can't actually affect the expressions in the method body itself.
If we proceed with conditional evaluation, user education will be a key component. The proposal does not involve conditional evaluation when
the interpolated string literal is used directly as a string
, but other types can introduce this. If a user is confused by this behavior,
we need a clear thing we can point out to say "this is why you're seeing the current behavior." One potential way to do this is by introducing
a specific syntax to enable conditional interpolation hole evaluation, something like $?"{ConditionallyEvaluated()}"
. With such a syntax,
we would never conditionally evaluate the holes unless the string were marked with $?
or @$?
. However, this immediately becomes a concern
for libraries that want to introduce conditional evaluation: presumably they were making that decision for a good reason. A logging library
would want this to just work, and every library that uses the feature would presumably then also need to make an analyzer to go along with the
feature to ensure their customers are actually using it.
We also considered the benefits that partial evaluation gives the user. An important goal for C# features is that users don't adopt the
feature then immediately switch to something else because it introduces performance issues. Patterns are a good example of doing this right:
the compiler generates code that is usually as good, if not better, than the code the user would write by hand to match a pattern. Today's
interpolated strings, on the other hand, do not do this today. They box value types, don't work with spans, and generate array garbage that
can't be elided. We want interpolated strings to be better: using the language feature should generate code that is just as performant as
you can get manually today. Conditional evaluation is a big part of this: both with the up front check, and with the intervening checks on
each Append
call, we can ensure that a simple line of C# generates code that is as good as manual checks.
A good analogy to think about for concerns about conditional evaluation is in struct
s: if C# were to introduce value types today, would
we be concerned that we need to call out the copies everywhere they can occur? Or would we simply accept that, yes, struct copies can happen
and that it's fine. Yes, behavior could change inside method bodies on upgrading a library, but that can happen in a number of ways with any
library upgrade today. New instance methods can be introduced that cause extension methods to longer be chosen, libraries can introduce new
conversions, new exceptions can be thrown/existing exceptions can be no longer thrown. While is another change that can happen, we don't think
it's substantially different enough to warrant concern.
We accept conditional evaluation of interpolation holes as written in the spec.