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
我们通常认为,对于执行不同操作的方法,库主要使用不同的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<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)
您不能强制编译器选择 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 的额外重载并不是真正必要的,但会简化查找使用插值字符串的位置)