在VS 2012和VS 2015之间处理F#模糊通用接口导致后者编译错误的区别 [英] Difference between treatment of ambiguous generic interfaces by F# between VS 2012 and VS 2015 leading to compile errors in the latter

查看:155
本文介绍了在VS 2012和VS 2015之间处理F#模糊通用接口导致后者编译错误的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

从VS 2012迁移F#项目后到VS 2015我在接口的某些用法上收到一个错误。特别是它发生在一个类型实现两个通用接口的地方。我知道这是不允许直接在F#中,但这种类型来自C#。

重现该问题:



< H2> 1。类型定义在C#中:

将其粘贴到某个类库中。

> public interface Base {}

public interface IPrime< T> :Base
{
T Value {get; }
}


公共接口IFloat:IPrime< double>
{
}

公共接口IInt:IFloat,IPrime< int>
{
int薪水{get; }
}

公共抽象类Prime< T> :IPrime< T>
{
public T Value {get;受保护的内部设置}

public static implicit operator T(Prime< T> value)
{
return value.Value;




公共类FFloat:Prime< double>,IFloat
{
public FFloat(double value)
{
this.Value = value;
}
public double Salary {get;组; }

$ b $ public class FInt:Prime< int> ;, IInt
{
public FInt(int value)
{
this。价值=价值;
}
public int薪水{get;组; }
int IPrime< int> .Value {get {return this.Value; }}
double IPrime< double> .Value {get {return this.Value; }}
}



2。在F#中使用类型:



在Visual Studio 2012中的使用,正常工作:

 打开SomeClassLib 

[< EntryPoint>]
let main argv =
let i = new FInt(10)
let f = new FFloat(12.0 )
let g = fun(itm:SomeClassLib.Base) - >

|匹配:? IInt as i - > i.Value
| :? IFloat as i - > i.Value |> int
| _ - > failwitherror

如果您在Visual Studio 2015中打开相同的解决方案,则会收到错误


错误FS0001:类型不匹配。期待浮动 - > float 但给出 float - > int 。类型'float'不匹配类型'int'


这当然很容易通过类型转换来纠正,但是,然后,出人意料的是,它不再在Visual Studio 2012中加载(好吧,有两种方法可以使它工作,在这个例子中它是微不足道的)。



3。结果



如果您将鼠标悬停在 i.Value 上,您会得到:



Visual Studio 2012

  | :? IInt as i  - > i.Value //值是int 
| :? IFloat as i - > i.Value |> int // Value is float

Visual Studio 2015

  | :? IInt as i  - > i.Value //值是浮动
| :? IFloat as i - > i.Value |> int // Value is float

我想知道这个区别在哪里。



问题/备注



我觉得奇怪的是,编译器似乎选择一个赋值工作,另一个没有,这会招致无意的,有时很难得到的错误。坦率地说,我有点担心,在类型推理中可以选择int和float之间的关系,现在它会支持float,它在过去是int。



2012年和2015年之间的差异似乎是,前者在进入等级体系时遇到了第一次碰撞,而后者似乎采取了最后一次,但我一直无法明确证实这一点。



这是现有功能的缺陷还是改进?恐怕我会做一些重新设计来消除模糊性,除非有人知道一个简单的方法来处理这个问题(它发生在大约50个左右的地方,可以用手修复,但不是很好) ?

初步结论



我很清楚原始类型可以被认为是模棱两可的,但.NET语言支持它,MSIL也支持它。



我知道F#不支持在同一个方法或属性上混合泛型类型,这是我可以接受的语言选择,但是它的类型推断做出了一个决定:无论如何,我认为这应该是一个错误,类似于您在解决F#中的重载成员时获得的错误。 ,在这种情况下,错误是非常明确的,并列出了选项。

解决方案

我交换了C#代码中的接口顺序这个和下面的代码在VS 2013中编译

  public interface IInt:IPrime< int> ,IFloat 
{
int薪水{get; }
}



  let g = fun(itm:Base) - > 

|匹配:? IInt as i - > i.Value
| :? IFloat as i - > i.Value |> int
| _ - > failwitherror

我怀疑其中一个'Value'成员是隐藏的(被FSharp的视角所掩盖)由另一个,基于接口的顺序。



我把你的模式匹配编码成这样,并且验证界面顺序并不重要,在这种情况下。

  let g = fun(itm:Base) - > 

|匹配:? IPrime< INT>因为我 - > i.Value
| :? IPrime<浮动>因为我 - > i.Value |> int
| _ - > failwitherror

对我来说,这似乎是实现细节的变化。我不确定我是否会称它为错误。如果这是一个错误,F#规范将是最后一个词。

(Note: Question updated with full reproducible example)

After migrating an F# project from VS 2012 to VS 2015 I receive an error on certain usages of interfaces. In particular it happens where a type implements two generic interfaces. I know this is not allowed in F# directly, but this type comes from C#.

To reproduce the problem:

1. type definition in C#:

Paste this in some class library.

public interface Base { }

public interface IPrime<T> : Base
{
    T Value { get; }
}


public interface IFloat : IPrime<double>
{
}

public interface IInt : IFloat, IPrime<int>
{
    int Salary { get; }
}

public abstract class Prime<T> : IPrime<T>
{
    public T Value { get; protected internal set; }

    public static implicit operator T(Prime<T> value)
    {
        return value.Value;
    }
}


public class FFloat : Prime<double>, IFloat
{
    public FFloat(double value)
    {
        this.Value = value;
    }
    public double Salary { get; set; }
}

public class FInt : Prime<int>, IInt
{
    public FInt(int value)
    {
        this.Value = value;
    }
    public int Salary { get; set; }
    int IPrime<int>.Value { get { return this.Value; } }
    double IPrime<double>.Value { get { return this.Value; } }
}

2. Usage of types in F#:

Usage in Visual Studio 2012, working:

open SomeClassLib

[<EntryPoint>]
let main argv = 
    let i = new FInt(10)
    let f = new FFloat(12.0)
    let g = fun (itm: SomeClassLib.Base) -> 
        match itm with
        | :? IInt as i -> i.Value
        | :? IFloat as i -> i.Value |> int
        | _ -> failwith "error"

If you open the same solution in Visual Studio 2015 you get the error

error FS0001: Type mismatch. Expecting a float -> float but given a float -> int.The type 'float' does not match the type 'int'

This is of course easily corrected by a typecast, but then, surprise surprise, it won't load in Visual Studio 2012 anymore (well, there are ways to get it working in both, and in this example it is trivial).

3. Findings

If you hover over the i.Value you get:

Visual Studio 2012

| :? IInt as i -> i.Value          // Value is int
| :? IFloat as i -> i.Value |> int // Value is float

Visual Studio 2015

| :? IInt as i -> i.Value          // Value is float
| :? IFloat as i -> i.Value |> int // Value is float

I'm wondering where this difference comes from.

Questions / remarks

I find it strange that the compiler seems to "choose" that one assignment works, and another does not, this invites for inadvertent and sometimes hard-to-get mistakes. And frankly, I'm a little worried that in places were type inference can choose between int and float, it will now favor float, where it was int in the past.

The difference between the 2012 and 2015 seems to be that the former takes the first in encounters when going up the hierarchy, and the latter seems to take the last, but I haven't been able to unambiguously confirm that.

Is this a bug or an improvement of an existing feature? I'm afraid I'll have some redesigning to do to remove the ambiguity, unless someone knows of a simple way to deal with this (it happens in only about 50 or so places, doable to fix by hand, but not so nice)?

Preliminary conclusion

It is clear to me that the original type can be considered ambiguous and possibly poor design, but .NET languages support it and so does MSIL.

I know that F# does not support mixing generic types on the same method or property, which is a language choice I can live with, but its type inference makes a decision that cannot be predicted, which I think is a bug.

Either way, I believe this should be an error, similar to the one you get when you address overloaded members in F#, in which case the error is very clear and lists the options.

解决方案

I swapped the order of the interfaces in the C# code to this and the following code compiled as in VS 2013

public interface IInt : IPrime<int>, IFloat
{
   int Salary { get; }
}

let g = fun (itm: Base) -> 
    match itm with
    | :? IInt as i -> i.Value
    | :? IFloat as i -> i.Value |> int
    | _ -> failwith "error"

I suspect that one of the 'Value' members is hidden (shadowed by FSharp's perspective) by the other, based on the order of the interfaces.

I coded your pattern matching like this instead, and validated that the interface order did not matter, in this case.

let g = fun (itm: Base) -> 
    match itm with
    | :? IPrime<int> as i -> i.Value
    | :? IPrime<float> as i -> i.Value |> int
    | _ -> failwith "error"

To me, this seems like a change in the implementation details. I am not sure if I would call it a bug. The F# specification would be the final word if this is a bug.

这篇关于在VS 2012和VS 2015之间处理F#模糊通用接口导致后者编译错误的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆