隐式转换的重载解决方案
Overload Resolution with implicit conversions
我基本上想要 string/FormattableString 两个单独的重载(背景是我想推动人们对日志消息使用字符串常量并通过结构化日志而不是日志消息传递参数以简化分析。所以FormattableString 日志记录方法将被废弃)。
现在由于编译器的工作方式,您不能直接重载这些方法,因为 FormattableString 在传递之前会转化为字符串。有效的是有一个定义隐式重载的包装器结构:
public struct StringIfNotFormattableStringAdapter
{
public string StringValue { get; }
private StringIfNotFormattableStringAdapter(string s)
{
StringValue = s;
}
public static implicit operator StringIfNotFormattableStringAdapter(string s)
{
return new StringIfNotFormattableStringAdapter(s);
}
public static implicit operator StringIfNotFormattableStringAdapter(FormattableString fs)
{
throw new InvalidOperationException("This only exists to allow correct overload resolution. " +
"This should never be called since the FormattableString overload should be preferred to this.");
}
}
public static class Test
{
public static void Log(StringIfNotFormattableStringAdapter msg)
{
}
public static void Log(FormattableString msg)
{
}
public static void Foo()
{
Log("Hello"); // resolves to StringIfNotFormattableStringAdapter overload
Log($"Hello"); // resolves to FormattableString overload
}
}
到目前为止一切顺利。
我不明白的是:为什么删除
implicit operator StringIfNotFormattableStringAdapter(FormattableString fs)
导致调用Log($"Hello")
变得不明确?
CS0121 The call is ambiguous between the following methods or properties: Test.Log(StringIfNotFormattableStringAdapter)' and 'Test.Log(FormattableString)'`
根据 C# 规范,Interpolated strings,存在从内插字符串到 FormattableString
的隐式转换:
An interpolated_string_expression is classified as a value. If it is
immediately converted to System.IFormattable
or
System.FormattableString
with an implicit interpolated string
conversion (Implicit interpolated string conversions), the
interpolated string expression has that type. Otherwise, it has the
type string
.
在提供的代码中,还有 string
到 StringIfNotFormattableStringAdapter
的转换。
方法调用
Log($"Hello");
可以解析为两种Log
方法,因为内插字符串表达式$"Hello"
可以是:
- 隐式转换为
FormattableString
作为内插字符串;
- 隐式转换为
StringIfNotFormattableStringAdapter
作为 string
。
此处编译器出现歧义,它需要额外的规则来解决这种歧义。为了解决歧义,编译器使用 C# 规范 Better Conversion Target (go to the bottom of the page 164) 中描述的规则。规则说:
Given two different types T1
and T2
, T1
is a better conversion
target than T2
if no implicit conversion from T2
to T1
exists,
and at least one of the following holds:
An implicit conversion from T1
to T2
exists
(other rules are not important for our case)
在提供的代码中,FormattableString
比 StringIfNotFormattableStringAdapter
转换更好,因为
- 没有从
StringIfNotFormattableStringAdapter
到 FormattableString
的隐式转换
和
- 存在从
FormattableString
到 StringIfNotFormattableStringAdapter
的隐式转换。
因此编译器更喜欢将内插字符串 $"Hello"
转换为 FormattableString
然后调用方法 Log(FormattableString)
.
Why does removing the
implicit operator StringIfNotFormattableStringAdapter(FormattableString fs)
cause the call Log($"Hello")
to become ambiguous?
因为当您删除此运算符时,第二条规则 ("an implicit conversion from FormattableString
to StringIfNotFormattableStringAdapter
exists") 会中断,现在编译器无法定义更好的转换目标。这会导致编译器出现歧义并发生编译错误。
我基本上想要 string/FormattableString 两个单独的重载(背景是我想推动人们对日志消息使用字符串常量并通过结构化日志而不是日志消息传递参数以简化分析。所以FormattableString 日志记录方法将被废弃)。
现在由于编译器的工作方式,您不能直接重载这些方法,因为 FormattableString 在传递之前会转化为字符串。有效的是有一个定义隐式重载的包装器结构:
public struct StringIfNotFormattableStringAdapter
{
public string StringValue { get; }
private StringIfNotFormattableStringAdapter(string s)
{
StringValue = s;
}
public static implicit operator StringIfNotFormattableStringAdapter(string s)
{
return new StringIfNotFormattableStringAdapter(s);
}
public static implicit operator StringIfNotFormattableStringAdapter(FormattableString fs)
{
throw new InvalidOperationException("This only exists to allow correct overload resolution. " +
"This should never be called since the FormattableString overload should be preferred to this.");
}
}
public static class Test
{
public static void Log(StringIfNotFormattableStringAdapter msg)
{
}
public static void Log(FormattableString msg)
{
}
public static void Foo()
{
Log("Hello"); // resolves to StringIfNotFormattableStringAdapter overload
Log($"Hello"); // resolves to FormattableString overload
}
}
到目前为止一切顺利。
我不明白的是:为什么删除
implicit operator StringIfNotFormattableStringAdapter(FormattableString fs)
导致调用Log($"Hello")
变得不明确?
CS0121 The call is ambiguous between the following methods or properties: Test.Log(StringIfNotFormattableStringAdapter)' and 'Test.Log(FormattableString)'`
根据 C# 规范,Interpolated strings,存在从内插字符串到 FormattableString
的隐式转换:
An interpolated_string_expression is classified as a value. If it is immediately converted to
System.IFormattable
orSystem.FormattableString
with an implicit interpolated string conversion (Implicit interpolated string conversions), the interpolated string expression has that type. Otherwise, it has the typestring
.
在提供的代码中,还有 string
到 StringIfNotFormattableStringAdapter
的转换。
方法调用
Log($"Hello");
可以解析为两种Log
方法,因为内插字符串表达式$"Hello"
可以是:
- 隐式转换为
FormattableString
作为内插字符串; - 隐式转换为
StringIfNotFormattableStringAdapter
作为string
。
此处编译器出现歧义,它需要额外的规则来解决这种歧义。为了解决歧义,编译器使用 C# 规范 Better Conversion Target (go to the bottom of the page 164) 中描述的规则。规则说:
Given two different types
T1
andT2
,T1
is a better conversion target thanT2
if no implicit conversion fromT2
toT1
exists, and at least one of the following holds:
An implicit conversion from
T1
toT2
exists(other rules are not important for our case)
在提供的代码中,FormattableString
比 StringIfNotFormattableStringAdapter
转换更好,因为
- 没有从
StringIfNotFormattableStringAdapter
到FormattableString
的隐式转换
和
- 存在从
FormattableString
到StringIfNotFormattableStringAdapter
的隐式转换。
因此编译器更喜欢将内插字符串 $"Hello"
转换为 FormattableString
然后调用方法 Log(FormattableString)
.
Why does removing the
implicit operator StringIfNotFormattableStringAdapter(FormattableString fs)
cause the call
Log($"Hello")
to become ambiguous?
因为当您删除此运算符时,第二条规则 ("an implicit conversion from FormattableString
to StringIfNotFormattableStringAdapter
exists") 会中断,现在编译器无法定义更好的转换目标。这会导致编译器出现歧义并发生编译错误。