将struct更改为sealed类时,隐式转换失败

Moo*_*eef 13 c# struct class implicit-conversion

有问题的结构/类:

public struct HttpMethod
{
    public static readonly HttpMethod Get = new HttpMethod("GET");
    public static readonly HttpMethod Post = new HttpMethod("POST");
    public static readonly HttpMethod Put = new HttpMethod("PUT");
    public static readonly HttpMethod Patch = new HttpMethod("PATCH");
    public static readonly HttpMethod Delete = new HttpMethod("DELETE");

    private string _name;

    public HttpMethod(string name)
    {
        // validation of name
        _name = name.ToUpper();
    }

    public static implicit operator string(HttpMethod method)
    {
        return method._name;
    }

    public static implicit operator HttpMethod(string method)
    {
        return new HttpMethod(method);
    }

    public static bool IsValidHttpMethod(string method)
    {
        // ...
    }

    public override bool Equals(object obj)
    {
        // ...
    }

    public override int GetHashCode()
    {
        return _name.GetHashCode();
    }

    public override string ToString()
    {
        return _name;
    }
}
Run Code Online (Sandbox Code Playgroud)

以下代码触发了此问题:

public class HttpRoute
{
    public string Prefix { get; }
    public HttpMethod[] Methods { get; }

    public HttpRoute(string pattern, params HttpMethod[] methods)
    {
        if (pattern == null) throw new ArgumentNullException(nameof(pattern));
        Prefix = pattern;
        Methods = methods ?? new HttpMethod[0];
    }

    public bool CanAccept(HttpListenerRequest request)
    {
        return Methods.Contains(request.HttpMethod) && request.Url.AbsolutePath.StartsWith(Prefix);
    }
}
Run Code Online (Sandbox Code Playgroud)

通过将HttpMethod结构更改为密封类来创建编译器错误.报告错误return Methods.Contains(request.HttpMethod),注意:request.HttpMethod在这种情况下是a string.产生以下内容:

Error   CS1929  'HttpMethod[]' does not contain a definition for 'Contains' and the best extension method overload 'Queryable.Contains<string>(IQueryable<string>, string)' requires a receiver of type 'IQueryable<string>'
Run Code Online (Sandbox Code Playgroud)

我的问题是为什么?我可以重新设计代码以使其工作,但我想知道为什么从struct更改为sealed类会产生这种奇怪的错误.

编辑:添加一组简化的示例代码(可在此处获取:https://dotnetfiddle.net/IZ9OXg).注意到,评论第二类的隐性操作字符串允许代码进行编译:

public static void Main()
{
    HttpMethod1[] Methods1 = new HttpMethod1[10];
    HttpMethod2[] Methods2 = new HttpMethod2[10];

    var res1 = Methods1.Contains("blah"); //works
    var res2 = Methods2.Contains("blah"); //doesn't work
}

public struct HttpMethod1
{
    public static implicit operator HttpMethod1(string method)
    {
        return new HttpMethod1();
    }

    public static implicit operator string (HttpMethod1 method)
    {
        return "";
    }

}

public class HttpMethod2
{
    public static implicit operator HttpMethod2(string method)
    {
        return new HttpMethod2();
    }

    //Comment out this method and it works fine
    public static implicit operator string (HttpMethod2 method)
    {
        return "";
    }

}
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 10

我知道的事情:

  • 显然问题在于类型推断.
  • 在第一种情况下,T被推断为HttpMethod1.
  • 在结构案例中,没有转换HttpMethod1[],IEnumerable<string>因为协方差仅适用于引用类型.
  • 在类的情况下,没有转换HttpMethod2[],IEnumerable<string>因为协方差仅适用于引用转换,这是用户定义的转换.

我怀疑但需要确认的事情:

  • 关于我的最后两点之间的细微差别的一点是混淆了类型推断算法.

更新:

  • 它与协变阵列转换无关.即使没有数组转换,问题也会重现.
  • 然而,它确实与协变接口转换有关.
  • 它与字符串无关.(字符串通常有点奇怪,因为它们有一个难以记住的转换IEnumerable<char>,偶尔会混淆类型推断.)

这是一个显示问题的程序片段; 更新您的转化以转换为C而不是字符串:

public interface IFoo<out T> {}
public class C {}
public class Program
{
    public static bool Contains<T>(IFoo<T> items, T item) 
    {
        System.Console.WriteLine(typeof(T));
        return true; 
    }
    public static void Main()
    {
        IFoo<HttpMethod1> m1 = null;
        IFoo<HttpMethod2> m2 = null;
        var res1 = Contains(m1, new C()); //works
        var res2 = Contains(m2, new C()); //doesn't work
    }
    }
Run Code Online (Sandbox Code Playgroud)

这看起来像是类型推断中的一个可能的错误,如果是,那就是我的错; 如果是这样的话会有很多道歉.可悲的是,我今天没有时间进一步研究它.你可能想在github上打开一个问题,让一个人仍然以此为生.我会着迷于了解结果是什么,以及它是否是设计或推理算法实现中的错误.

  • 我太棒了,希望赏金会吸引你回答这个问题!谢谢! (3认同)
  • @Evk你有机会运行Resharper吗?禁用它,您应该只看到第二个突出显示. (2认同)