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

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

想象一下:

 static class Log { static void Debug(string message); static void Debug(IFormattable message); static bool IsDebugEnabled { get; } } 

我有非常昂贵的ToString()对象。 以前,我做过以下事情:

 if (Log.IsDebugEnabled) Log.Debug(string.Format("Message {0}", expensiveObject)); 

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

 Log.Debug($"Message {expensiveObject}"); 

但是,这会调用Debug(string)重载。

这是Roslyn团队的慎重决定 :

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

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

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

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

您只需要欺骗编译器优先选择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(); public interface IStringOrFormattableStringNoOverload { void NoOverload(StringIfNotFormattableStringAdapter s); } private readonly IStringOrFormattableStringNoOverload _noOverload = Substitute.For(); [Fact] public void A_Literal_String_Interpolation_Hits_FormattableString_Overload() { _stringOrFormattableStringOverload.Overload($"formattable string"); _stringOrFormattableStringOverload.Received().Overload(Arg.Any()); } [Fact] public void A_String_Hits_StringIfNotFormattableStringAdapter_Overload() { _stringOrFormattableStringOverload.Overload("plain string"); _stringOrFormattableStringOverload.Received().Overload(Arg.Any()); } [Fact] public void An_Explicit_FormattableString_Detects_Missing_FormattableString_Overload() { Assert.Throws( () => _noOverload.NoOverload((FormattableString) $"this is not allowed")); } } 

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

 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"); } } 

您需要将其IFormattable FormattableStringIFormattableFormattableString

 Log.Debug((IFormattable)$"Message {expensiveObject}"); 

您可以使用IFormattable技巧作为IFormattableIFormattable的速记:

 public static class FormattableExtensions { public static FormattableString FS(FormattableString formattableString) { return formattableString; } } 

并以这种方式使用它:

 Log.Debug(FS($"Message {expensiveObject}")); 

我希望JIT编译器在生产中内联FS