Skip to content

[Proposal] Capture names (or full code) of interpolated values if string interpolation is passed in as a FormattableString #142

@ashmind

Description

@ashmind

This proposal was already answered on CodePlex with "too late for C#6", but since C#7 is taking input at the moment, I think it is worth mentioning this again.

Problem:

Current design for FormattableString is:

public class FormattableString {
    public string Format { get; }
    public object[] Args { get; }
    // ...
}

Consider two following usages:

// Some future Serilog:
log.Information($"Processed {position} in {elapsedMs:000} ms.");

// Some future Dapper:
connection.Execute($"SELECT * FROM T1 WHERE Id = {id}");

In addition to saving the log string, Serilog saves format values as properties on the log entry object. However with current FormattableString the names of these values (position, elapsedMs) would not be available.

Dapper does not really need the names (it can generate parameters named @p1 and @p2), but proper names can improve readability of SQL traces and general SQL debugging.

And another (weird) use case for capturing names of the provided values:

Argument.NotNull($"{x}"); // doesn't actually need the string at all, but uses arg name/value

Compare this with current:

Argument.NotNull(nameof(x), x); // more verbose, not DRY

Potential solution

Add IReadOnlyList<string> Names { get; } to the FormattableString.

Those could be generated based on anonymous class property name algorithm:

1. $"{x}, {y}"  // Names = "x", "y"
2. $"{x.Y.Z}"   // Names = "Z"
3. $"{x}, {5}"  // Names = "x", null

Or just literally contain passed in code as a string, e.g. $"{x+5}" // Names = "x+5", though in this case something like Snippets would be a better name.

Since each call site will have a fixed set of names/snippets, each list can be cached per call site once and reused between calls. This means that even the names/snippets aren't used, the performance cost would be minimal.

Pros
  1. Allows frameworks to be more intelligent when generating a new string from FormattableString or recording the parameters somewhere.
  2. Gives some unexpected uses to the FormattableString by making it a light-weight way to pass identifier-named value -- e.g. validation.
  3. If code is passed as as string instead of generating a name, this can be used to create an easily cacheable analog of expression trees (by parsing it with Roslyn etc), and various other kinds of 'magic' APIs.
Cons
  1. If names are generated using anonymous class algorithm, some of them might be null. This might be unexpected -- frameworks can provide fallback, but they have to be careful of those cases.
  2. If full code is passed as a string, analysis has to be careful so that it is not broken by small variations within the code (e.g. as introduced by refactoring).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions