generics - 非ジェネリック - where t new() vb net



F#によるVS2012とVS2015間のあいまいなジェネリックインタフェースの処理と、後者のコンパイルエラーにつながる相違点 (1)

(注:完全に再現可能な例で更新された質問)

F#プロジェクトをVS 2012からVS 2015に移行した後、インタフェースの特定の用途にエラーが発生します。 特に、型が2つの汎用インターフェースを実装する場合に起こります。 私はこれがF#では直接許されないことを知っていますが、このタイプはC#から来ています。

問題を再現するには。

1. C#での型定義:

これをいくつかのクラスライブラリに貼り付けます。

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. F#での型の使用:

Visual Studio 2012での使用方法:

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"

Visual Studio 2015で同じソリューションを開くと、エラーが発生します

エラーFS0001:型の不一致。 float -> float期待していますが、 float -> int'float'型が'int'型と一致しません

これはもちろん型キャストで簡単に修正できますが、驚いたことにVisual Studio 2012にはもうロードされません(両方とも動作させる方法がありますが、この例では簡単です)。

3.所見

i.Value上にi.Valuei.Valueと、 i.Valueようになります。

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

私はこの違いがどこから来るのだろうかと思っています。

質問/発言

私は、コンパイラが1つの割り当てが動作することを「選択する」ように見えるのは奇妙であり、別のものはそうではないということは、偶然、 そして、率直に言って、タイプ推論でintとfloatのどちらかを選択できる場所では、これが過去にintであったfloatが好都合になると少しは心配しています。

2012年と2015年の違いは、階層を上るときに前者が遭遇したときに最初のことであり、後者が最後を取るように見えるが、私はそれを明白に確認することができなかったようだ。

これはバグか既存の機能の改善ですか? 私は曖昧さを取り除くためにいくつかの再設計をするつもりです。誰かがこれに対処する簡単な方法を知っていない限り(それはわずか50か所程度しか起こらず、手で修正することができますが、 ?

予備的結論

元のタイプはあいまいであり、おそらくデザインが不良であると考えることができますが、.NET言語でサポートされているため、MSILもサポートしています。

F#は同じメソッドやプロパティでジェネリック型を混合することをサポートしていないことを知っています。これは私が暮らすことができる言語の選択ですが、その型推論は予測できない決定をします。

いずれにせよ、これはF#でオーバーロードされたメンバーに対処するときに得られるエラーと同様のエラーであるはずです。この場合、エラーは非常に明確でオプションをリストします。


私はC#コードのインターフェイスの順序をこれと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"

私は、 'Value'メンバーの1つが、インターフェイスの順序に基づいて、他のメンバーによって隠されている(FSharpの視点によって隠されている)と考えています。

代わりにこのようにパターンマッチングをコード化し、この場合はインターフェイスの順序が重要でないことを検証しました。

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

私にとって、これは実装の詳細の変更のようです。 私はそれをバグと呼ぶかどうか分からない。 これがバグであれば、F#の仕様が最終的な単語になります。





visual-studio-2015