c# - 使用隐式转换重载解析

标签 c# language-lawyer overload-resolution

我基本上想要 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作为内插字符串;
  • 隐式转换为 StringIfNotFormattableStringAdapterstring .

  • 这里编译器出现歧义,它需要额外的规则来解决这种歧义。为了解决歧义,编译器使用 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)



    在提供的代码中 FormattableStringStringIfNotFormattableStringAdapter 更好的转换因为
  • 没有来自 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?



    因为当您删除此运算符时,第二条规则(“存在从 FormattableStringStringIfNotFormattableStringAdapter 的隐式转换存在”)中断,现在编译器无法定义更好的转换目标。这会导致编译器产生歧义并发生编译错误。

    关于c# - 使用隐式转换重载解析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60778208/

    相关文章:

    html - 什么格式化上下文适用于不创建自己的元素?

    c++ - 为什么 std::string_view 在三元表达式中创建悬空 View ?

    c++ - 转换为指向模板的指针是否会实例化该模板?

    c++ - 如何让编译器选择非成员函数重载

    c++ - 当 T = int& 时,为什么构造函数 Message(const T& data) 与 Message(T&& data) 冲突?

    c# - 使用套接字的简单 Http 代理 : Questions

    c# - 如何在 ASP.NET Core Identity 的 Controller 构造函数中获取登录用户名

    c# - ASP.net 防止在编辑模式下单击 gridview 行

    c# - 使用 LINQ 遍历列表并设置 founded 变量

    c++ - 为什么在传递 long long 时调用具有两个 double 类型参数的重载函数?