使用字符串插值重载的字符串方法

not*_*row 12 c# string-interpolation c#-6.0

为什么字符串插值更喜欢方法的重载string而不是IFormattable

想象一下:

static class Log {
    static void Debug(string message);
    static void Debug(IFormattable message);
    static bool IsDebugEnabled { get; }
}
Run Code Online (Sandbox Code Playgroud)

我有非常昂贵的物品ToString().以前,我做过以下事情:

if (Log.IsDebugEnabled) Log.Debug(string.Format("Message {0}", expensiveObject));
Run Code Online (Sandbox Code Playgroud)

现在,我希望内部有IsDebugEnabled逻辑Debug(IFormattable),并且仅在必要时才在消息中的对象上调用ToString().

Log.Debug($"Message {expensiveObject}");
Run Code Online (Sandbox Code Playgroud)

然而,这称为Debug(string)过载.

Raw*_*ing 10

这是Roslyn团队慎重决定:

我们通常认为,对于执行不同操作的方法,库主要使用不同的API名称编写.因此,FormattableString和String之间的重载分辨率差异无关紧要,因此字符串也可能获胜.因此,我们应该坚持插值字符串是一个字符串的简单原则.故事结局.

在链接中有关于此的更多讨论,但结果是他们希望您使用不同的方法名称.

一些库API确实希望消费者使用FormattableString,因为它更安全或更快.采用字符串的API和采用FormattableString的API实际上做了不同的事情,因此不应该重载相同的名称.

  • 哇这经常使用时很烦人. (3认同)
  • 太糟糕的构造函数不能有不同的名称.考虑到这些人写语言并且可能理解这一点,这些都是微弱的理由. (3认同)

Rob*_*ahl 8

意识到你问为什么你不能这样做,我只想指出你事实上可以做到这一点.

您只需要欺骗编译器优先使用FormattableString重载.我在这里详细解释了它:https://robertengdahl.blogspot.com/2016/08/how-to-overload-string-and.html

这是测试代码:

public class StringIfNotFormattableStringAdapterTest
{
    public interface IStringOrFormattableStringOverload
    {
        void Overload(StringIfNotFormattableStringAdapter s);
        void Overload(FormattableString s);
    }

    private readonly IStringOrFormattableStringOverload _stringOrFormattableStringOverload =
        Substitute.For<IStringOrFormattableStringOverload>();

    public interface IStringOrFormattableStringNoOverload
    {
        void NoOverload(StringIfNotFormattableStringAdapter s);
    }

    private readonly IStringOrFormattableStringNoOverload _noOverload =
        Substitute.For<IStringOrFormattableStringNoOverload>();

    [Fact]
    public void A_Literal_String_Interpolation_Hits_FormattableString_Overload()
    {
        _stringOrFormattableStringOverload.Overload($"formattable string");
        _stringOrFormattableStringOverload.Received().Overload(Arg.Any<FormattableString>());
    }

    [Fact]
    public void A_String_Hits_StringIfNotFormattableStringAdapter_Overload()
    {
        _stringOrFormattableStringOverload.Overload("plain string");
        _stringOrFormattableStringOverload.Received().Overload(Arg.Any<StringIfNotFormattableStringAdapter>());
    }

    [Fact]
    public void An_Explicit_FormattableString_Detects_Missing_FormattableString_Overload()
    {
        Assert.Throws<InvalidOperationException>(
            () => _noOverload.NoOverload((FormattableString) $"this is not allowed"));
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是使这项工作的代码:

public class StringIfNotFormattableStringAdapter
{
    public string String { get; }

    public StringIfNotFormattableStringAdapter(string s)
    {
        String = s;
    }

    public static implicit operator StringIfNotFormattableStringAdapter(string s)
    {
        return new StringIfNotFormattableStringAdapter(s);
    }

    public static implicit operator StringIfNotFormattableStringAdapter(FormattableString fs)
    {
        throw new InvalidOperationException(
            "Missing FormattableString overload of method taking this type as argument");
    }
}
Run Code Online (Sandbox Code Playgroud)


Run*_*une 7

您不能强制编译器选择 IFormattable/FormattableString 而不是 String,但可以让它选择 IFormattable/FormattableString 而不是 Object:

static class Log
{
    static void Debug(object message);
    static void Debug(IFormattable message);
    static void Debug(FormattableString message);
    static bool IsDebugEnabled { get; }
}
Run Code Online (Sandbox Code Playgroud)

此解决方案的成本是在采用 Object.get() 的方法中进行额外的 ToString() 调用。(FormattableString 的额外重载并不是真正必要的,但会简化查找使用插值字符串的位置)